yanghao пре 1 недеља
родитељ
комит
32edf16e24
1 измењених фајлова са 200 додато и 49 уклоњено
  1. 200 49
      src/views/researchProject/list/index.vue

+ 200 - 49
src/views/researchProject/list/index.vue

@@ -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;