|
|
@@ -126,7 +126,7 @@
|
|
|
/>
|
|
|
</ContentWrap>
|
|
|
|
|
|
- <!-- 新增/编辑/查看 弹窗 -->
|
|
|
+ <!-- 新增/编辑 弹窗 -->
|
|
|
<ResearchProjectDialog
|
|
|
v-model="dialogVisible"
|
|
|
:title="dialogTitle"
|
|
|
@@ -139,7 +139,6 @@
|
|
|
v-loading="formLoading"
|
|
|
:model="formData"
|
|
|
:rules="formRules"
|
|
|
- :disabled="isViewMode"
|
|
|
label-width="112px"
|
|
|
class="project-form"
|
|
|
>
|
|
|
@@ -240,7 +239,7 @@
|
|
|
<div class="schedule-editor">
|
|
|
<div class="schedule-toolbar">
|
|
|
<span>按阶段填写工作计划与截止时间</span>
|
|
|
- <el-button v-if="!isViewMode" type="primary" plain size="small" @click="addSchedule">
|
|
|
+ <el-button type="primary" plain size="small" @click="addSchedule">
|
|
|
<Icon icon="ep:plus" class="mr-5px" />增加工作
|
|
|
</el-button>
|
|
|
</div>
|
|
|
@@ -262,7 +261,7 @@
|
|
|
/>
|
|
|
</template>
|
|
|
</zm-table-column>
|
|
|
- <zm-table-column v-if="!isViewMode" label="操作" width="90" align="center">
|
|
|
+ <zm-table-column label="操作" width="90" align="center">
|
|
|
<template #default="{ $index }">
|
|
|
<el-button link type="danger" @click="removeSchedule($index)">
|
|
|
<Icon icon="ep:delete" />
|
|
|
@@ -276,7 +275,7 @@
|
|
|
<div class="budget-editor">
|
|
|
<div class="budget-toolbar">
|
|
|
<span>请按财务科目录入项目预算,金额需填写到元。</span>
|
|
|
- <el-button v-if="!isViewMode" type="primary" plain size="small" @click="addBudgetDetail">
|
|
|
+ <el-button type="primary" plain size="small" @click="addBudgetDetail">
|
|
|
<Icon icon="ep:plus" class="mr-5px" />新增预算科目
|
|
|
</el-button>
|
|
|
</div>
|
|
|
@@ -321,7 +320,7 @@
|
|
|
/>
|
|
|
</template>
|
|
|
</zm-table-column>
|
|
|
- <zm-table-column v-if="!isViewMode" label="操作" width="90" align="center">
|
|
|
+ <zm-table-column label="操作" width="90" align="center">
|
|
|
<template #default="{ $index }">
|
|
|
<el-button
|
|
|
link
|
|
|
@@ -346,7 +345,6 @@
|
|
|
v-model="applicationFile"
|
|
|
:return-object="true"
|
|
|
:limit="10"
|
|
|
- :disabled="isViewMode"
|
|
|
:file-type="['doc', 'docx', 'pdf', 'png', 'jpg', 'jpeg']"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
@@ -355,7 +353,6 @@
|
|
|
v-model="openingReportFile"
|
|
|
:return-object="true"
|
|
|
:limit="10"
|
|
|
- :disabled="isViewMode"
|
|
|
:file-type="['doc', 'docx', 'pdf', 'png', 'jpg', 'jpeg']"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
@@ -364,7 +361,6 @@
|
|
|
v-model="otherFile"
|
|
|
:return-object="true"
|
|
|
:limit="10"
|
|
|
- :disabled="isViewMode"
|
|
|
:file-type="['doc', 'docx', 'pdf', 'xls', 'xlsx', 'jpg', 'png']"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
@@ -382,16 +378,160 @@
|
|
|
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
|
|
|
"
|
|
|
>
|
|
|
- <el-button @click="dialogVisible = false">
|
|
|
- {{ isViewMode ? '关闭' : '取消' }}
|
|
|
- </el-button>
|
|
|
- <el-button v-if="!isViewMode" type="primary" :loading="submitLoading" @click="submitForm">
|
|
|
+ <el-button @click="dialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="submitLoading" @click="submitForm">
|
|
|
保存项目
|
|
|
</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
</ResearchProjectDialog>
|
|
|
|
|
|
+ <!-- 查看详情抽屉 -->
|
|
|
+ <el-drawer
|
|
|
+ v-model="detailVisible"
|
|
|
+ title="查看科研项目"
|
|
|
+ size="50%"
|
|
|
+ direction="rtl"
|
|
|
+ destroy-on-close
|
|
|
+ >
|
|
|
+ <div v-loading="detailLoading" class="detail-container">
|
|
|
+ <el-descriptions :column="2" border>
|
|
|
+ <el-descriptions-item label="项目名称">
|
|
|
+ {{ detailData.projectName }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="项目级别">
|
|
|
+ <dict-tag
|
|
|
+ v-if="detailData.projectLevel"
|
|
|
+ :type="DICT_TYPE.PROJECT_LEVEL"
|
|
|
+ :value="detailData.projectLevel"
|
|
|
+ />
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="项目类型">
|
|
|
+ <dict-tag
|
|
|
+ v-if="detailData.projectType"
|
|
|
+ :type="DICT_TYPE.PROJECT_TYPE"
|
|
|
+ :value="detailData.projectType"
|
|
|
+ />
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="执行单位">
|
|
|
+ {{ detailData.executingUnitName || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="项目负责人">
|
|
|
+ {{ detailData.projectLeader }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="项目组成员">
|
|
|
+ {{ detailData.teamMembers || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="预审状态">
|
|
|
+ <el-tag :type="getPreAuditTagType(detailData.preAuditStatus)">
|
|
|
+ {{ getPreAuditLabel(detailData.preAuditStatus) }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="正式立项状态">
|
|
|
+ <el-tag :type="getFormalAuditTagType(detailData.formalAuditStatus)">
|
|
|
+ {{ getFormalAuditLabel(detailData.formalAuditStatus) }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+
|
|
|
+ <el-divider content-position="left">项目内容</el-divider>
|
|
|
+ <el-descriptions :column="1" border>
|
|
|
+ <el-descriptions-item label="目标和意义">
|
|
|
+ {{ detailData.goalAndSignificance }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="主要研究内容">
|
|
|
+ {{ detailData.mainResearchContent }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="创新点">
|
|
|
+ {{ detailData.innovationPoint }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+
|
|
|
+ <el-divider content-position="left">进度安排</el-divider>
|
|
|
+ <zm-table :data="detailData.scheduleList" class="detail-table">
|
|
|
+ <zm-table-column type="index" label="#" width="56" align="center" />
|
|
|
+ <zm-table-column label="工作计划" prop="workPlan" min-width="200" show-overflow-tooltip />
|
|
|
+ <zm-table-column label="截止时间" prop="deadline" width="150">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <span>{{ row.deadline.join('-') }}</span>
|
|
|
+ </template>
|
|
|
+ </zm-table-column>
|
|
|
+ </zm-table>
|
|
|
+
|
|
|
+ <el-divider content-position="left">费用预算明细(元)</el-divider>
|
|
|
+ <zm-table :data="detailData.budgetList" class="detail-table">
|
|
|
+ <zm-table-column type="index" label="#" width="56" align="center" />
|
|
|
+ <zm-table-column
|
|
|
+ label="财务科目"
|
|
|
+ prop="financialSubject"
|
|
|
+ min-width="150"
|
|
|
+ show-overflow-tooltip
|
|
|
+ />
|
|
|
+ <zm-table-column label="预算金额(元)" prop="budgetAmount" width="150" align="right" />
|
|
|
+ <zm-table-column
|
|
|
+ label="预算说明"
|
|
|
+ prop="budgetDescription"
|
|
|
+ min-width="200"
|
|
|
+ show-overflow-tooltip
|
|
|
+ />
|
|
|
+ </zm-table>
|
|
|
+ <div class="budget-total">
|
|
|
+ <span>预算合计</span>
|
|
|
+ <strong>{{ computeBudgetTotal(detailData.budgetList).toFixed(2) }}</strong>
|
|
|
+ <span>元</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-divider content-position="left">附件信息</el-divider>
|
|
|
+ <el-descriptions :column="1" border>
|
|
|
+ <el-descriptions-item label="立项申请表">
|
|
|
+ <div v-if="detailApplicationFile.length">
|
|
|
+ <el-link
|
|
|
+ v-for="(file, index) in detailApplicationFile"
|
|
|
+ :key="index"
|
|
|
+ :href="file.filePath"
|
|
|
+ type="primary"
|
|
|
+ target="_blank"
|
|
|
+ class="mr-10px"
|
|
|
+ >
|
|
|
+ {{ file.fileName || `附件${index + 1}` }}
|
|
|
+ </el-link>
|
|
|
+ </div>
|
|
|
+ <span v-else>暂无</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="开题报告">
|
|
|
+ <div v-if="detailOpeningReportFile.length">
|
|
|
+ <el-link
|
|
|
+ v-for="(file, index) in detailOpeningReportFile"
|
|
|
+ :key="index"
|
|
|
+ :href="file.filePath"
|
|
|
+ type="primary"
|
|
|
+ target="_blank"
|
|
|
+ class="mr-10px"
|
|
|
+ >
|
|
|
+ {{ file.fileName || `附件${index + 1}` }}
|
|
|
+ </el-link>
|
|
|
+ </div>
|
|
|
+ <span v-else>暂无</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="其他文件">
|
|
|
+ <div v-if="detailOtherFile.length">
|
|
|
+ <el-link
|
|
|
+ v-for="(file, index) in detailOtherFile"
|
|
|
+ :key="index"
|
|
|
+ :href="file.filePath"
|
|
|
+ type="primary"
|
|
|
+ target="_blank"
|
|
|
+ class="mr-10px"
|
|
|
+ >
|
|
|
+ {{ file.name || `附件${index + 1}` }}
|
|
|
+ </el-link>
|
|
|
+ </div>
|
|
|
+ <span v-else>暂无</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+
|
|
|
<!-- 审批弹窗 -->
|
|
|
<el-dialog v-model="approveVisible" title="项目审批" width="600px">
|
|
|
<el-form :model="approveForm" label-width="80px">
|
|
|
@@ -475,7 +615,7 @@ type BudgetDetail = {
|
|
|
sort?: number
|
|
|
}
|
|
|
|
|
|
-// ========== 附件:三个独立 ref,完全不进 formData ==========
|
|
|
+// ========== 附件:表单使用 ==========
|
|
|
const applicationFile = ref<any[]>([])
|
|
|
const openingReportFile = ref<any[]>([])
|
|
|
const otherFile = ref<any[]>([])
|
|
|
@@ -581,15 +721,13 @@ const canSubmitFormal = (row: any) =>
|
|
|
|
|
|
const canDelete = (row: any) => row.preAuditStatus === 0 || row.preAuditStatus === 3
|
|
|
|
|
|
-// ========== 表单 ==========
|
|
|
+// ========== 表单(新增/编辑) ==========
|
|
|
const dialogVisible = ref(false)
|
|
|
const dialogTitle = ref('新增科研项目')
|
|
|
const formLoading = ref(false)
|
|
|
const submitLoading = ref(false)
|
|
|
const formRef = ref()
|
|
|
const editingId = ref<number>()
|
|
|
-// ⭐ 新增:只读模式标记
|
|
|
-const isViewMode = ref(false)
|
|
|
|
|
|
const createSchedule = (): ScheduleItem => ({ workPlan: '', deadline: '' })
|
|
|
const createBudgetDetail = (): BudgetDetail => ({
|
|
|
@@ -633,34 +771,21 @@ const budgetTotal = computed(() =>
|
|
|
formData.value.budgetList.reduce((sum, item) => sum + Number(item.budgetAmount || 0), 0)
|
|
|
)
|
|
|
|
|
|
-// ========== 打开表单(含只读模式) ==========
|
|
|
-/**
|
|
|
- * @param project 项目数据(有 id 表示编辑/查看)
|
|
|
- * @param mode 'view' 只读 | 'edit' 可编辑
|
|
|
- */
|
|
|
-const openForm = async (project?: any, mode: 'view' | 'edit' = 'edit') => {
|
|
|
+const computeBudgetTotal = (budgetList: BudgetDetail[]) => {
|
|
|
+ if (!budgetList) return 0
|
|
|
+ return budgetList.reduce((sum, item) => sum + Number(item.budgetAmount || 0), 0)
|
|
|
+}
|
|
|
+
|
|
|
+const openForm = async (project?: any) => {
|
|
|
dialogVisible.value = true
|
|
|
- isViewMode.value = mode === 'view'
|
|
|
- editingId.value = mode === 'edit' ? project?.id : undefined
|
|
|
+ editingId.value = project?.id
|
|
|
|
|
|
- if (isViewMode.value) {
|
|
|
- dialogTitle.value = '查看科研项目'
|
|
|
- } else {
|
|
|
- dialogTitle.value = project ? '编辑科研项目' : '新增科研项目'
|
|
|
- }
|
|
|
+ dialogTitle.value = project ? '编辑科研项目' : '新增科研项目'
|
|
|
|
|
|
formLoading.value = true
|
|
|
try {
|
|
|
if (project?.id) {
|
|
|
const data = await getProject(project.id)
|
|
|
-
|
|
|
- // ⭐ 从 attachments 里按 type 拆出 3 个数组
|
|
|
- const attachments = data.attachments || []
|
|
|
- const applicationFileData = attachments.filter((a: any) => a.type === 'APPLICATION')
|
|
|
- const openingReportFileData = attachments.filter((a: any) => a.type === 'OPENING_REPORT')
|
|
|
- const otherFileData = attachments.filter((a: any) => a.type === 'OTHER')
|
|
|
-
|
|
|
- // ⭐ 显式挑字段,避免 ...data 把 id、createTime、审批状态等脏数据带进 formData
|
|
|
formData.value = {
|
|
|
...createForm(),
|
|
|
projectLevel: data.projectLevel,
|
|
|
@@ -678,10 +803,11 @@ const openForm = async (project?: any, mode: 'view' | 'edit' = 'edit') => {
|
|
|
budgetList: data.budgetList?.length ? data.budgetList : [createBudgetDetail()]
|
|
|
}
|
|
|
|
|
|
- // ⭐ 附件回显到独立 ref
|
|
|
- applicationFile.value = applicationFileData
|
|
|
- openingReportFile.value = openingReportFileData
|
|
|
- otherFile.value = otherFileData
|
|
|
+ // 附件回显
|
|
|
+ const attachments = data.attachments || []
|
|
|
+ applicationFile.value = attachments.filter((a: any) => a.type === 'APPLICATION')
|
|
|
+ openingReportFile.value = attachments.filter((a: any) => a.type === 'OPENING_REPORT')
|
|
|
+ otherFile.value = attachments.filter((a: any) => a.type === 'OTHER')
|
|
|
} else {
|
|
|
formData.value = createForm()
|
|
|
resetAttachments()
|
|
|
@@ -691,9 +817,28 @@ const openForm = async (project?: any, mode: 'view' | 'edit' = 'edit') => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// ⭐ 查看:走只读模式
|
|
|
+// ========== 抽屉详情 ==========
|
|
|
+const detailVisible = ref(false)
|
|
|
+const detailLoading = ref(false)
|
|
|
+const detailData = ref<any>({})
|
|
|
+const detailApplicationFile = ref<any[]>([])
|
|
|
+const detailOpeningReportFile = ref<any[]>([])
|
|
|
+const detailOtherFile = ref<any[]>([])
|
|
|
+
|
|
|
const openDetail = async (row: any) => {
|
|
|
- await openForm(row, 'view')
|
|
|
+ detailVisible.value = true
|
|
|
+ detailLoading.value = true
|
|
|
+ try {
|
|
|
+ const data = await getProject(row.id)
|
|
|
+ detailData.value = data
|
|
|
+
|
|
|
+ const attachments = data.attachments || []
|
|
|
+ detailApplicationFile.value = attachments.filter((a: any) => a.type === 'APPLICATION')
|
|
|
+ detailOpeningReportFile.value = attachments.filter((a: any) => a.type === 'OPENING_REPORT')
|
|
|
+ detailOtherFile.value = attachments.filter((a: any) => a.type === 'OTHER')
|
|
|
+ } finally {
|
|
|
+ detailLoading.value = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// ========== 子表增删 ==========
|
|
|
@@ -704,9 +849,6 @@ const removeBudgetDetail = (index: number) => formData.value.budgetList.splice(i
|
|
|
|
|
|
// ========== 保存 ==========
|
|
|
const submitForm = async () => {
|
|
|
- // ⭐ 只读模式不允许提交
|
|
|
- if (isViewMode.value) return
|
|
|
-
|
|
|
const valid = await formRef.value?.validate().catch(() => false)
|
|
|
if (!valid) return
|
|
|
|
|
|
@@ -836,7 +978,6 @@ const removeProject = async (id: number) => {
|
|
|
|
|
|
// ========== 生命周期 ==========
|
|
|
onMounted(async () => {
|
|
|
- // 先获取用户信息拿 deptId,再加载列表(createForm 依赖 deptId)
|
|
|
const data = await getUserProfile()
|
|
|
deptId.value = data.dept.id
|
|
|
await getList()
|
|
|
@@ -874,7 +1015,8 @@ onMounted(async () => {
|
|
|
}
|
|
|
|
|
|
.budget-table,
|
|
|
-.schedule-table {
|
|
|
+.schedule-table,
|
|
|
+.detail-table {
|
|
|
width: 100%;
|
|
|
}
|
|
|
|
|
|
@@ -892,6 +1034,15 @@ onMounted(async () => {
|
|
|
font-size: 22px;
|
|
|
}
|
|
|
|
|
|
+.detail-container {
|
|
|
+ padding: 0 20px 20px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-container :deep(.el-divider__text) {
|
|
|
+ color: var(--el-color-primary);
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
@media (max-width: 900px) {
|
|
|
.budget-table {
|
|
|
min-width: 760px;
|