yanghao 1 неделя назад
Родитель
Сommit
4fbebfe4ad
1 измененных файлов с 88 добавлено и 22 удалено
  1. 88 22
      src/views/researchProject/list/index.vue

+ 88 - 22
src/views/researchProject/list/index.vue

@@ -347,27 +347,27 @@
       </div>
 
       <el-divider content-position="left">附件上传</el-divider>
-      <el-form-item label="立项申请表" required>
+      <el-form-item label="立项申请表">
         <UploadFile
-          v-model="formData.applicationFile"
+          v-model="applicationFile"
           :return-object="true"
-          :limit="1"
+          :limit="10"
           :file-type="['doc', 'docx', 'pdf', 'png', 'jpg', 'jpeg']"
         />
       </el-form-item>
-      <el-form-item label="开题报告" required>
+      <el-form-item label="开题报告">
         <UploadFile
-          v-model="formData.openingReportFile"
+          v-model="openingReportFile"
           :return-object="true"
-          :limit="1"
+          :limit="10"
           :file-type="['doc', 'docx', 'pdf', 'png', 'jpg', 'jpeg']"
         />
       </el-form-item>
       <el-form-item label="其他文件">
         <UploadFile
-          v-model="formData.otherFile"
+          v-model="otherFile"
           :return-object="true"
-          :limit="3"
+          :limit="10"
           :file-type="['doc', 'docx', 'pdf', 'xls', 'xlsx', 'jpg', 'png']"
         />
       </el-form-item>
@@ -472,6 +472,54 @@ type BudgetDetail = {
   sort?: number
 }
 
+// ========== 附件:三个独立 ref,完全不进 formData(与 index2.vue 一致) ==========
+const applicationFile = ref<any[]>([])
+const openingReportFile = ref<any[]>([])
+const otherFile = ref<any[]>([])
+
+/**
+ * 附件归一化:严格照抄 index2.vue
+ * - 不做任何额外字段补全(不加 name / url / fileName 等)
+ * - 输出结构就是 { filePath, type }
+ */
+const normalizeAttachments = (attachments: unknown, type?: string): any[] => {
+  if (!attachments) return []
+  const values = Array.isArray(attachments) ? attachments : [attachments]
+  return values
+    .flatMap((attachment: any) => {
+      if (typeof attachment === 'string') {
+        return attachment
+          .split(',')
+          .map((filePath) => filePath.trim())
+          .filter(Boolean)
+          .map((filePath) => (type ? { filePath, type } : filePath))
+      }
+      if (attachment && typeof attachment === 'object') {
+        const filePath =
+          attachment.filePath || attachment.path || attachment.url || attachment.fileUrl
+        return filePath ? [{ ...attachment, filePath, ...(type ? { type } : {}) }] : []
+      }
+      return []
+    })
+    .filter((attachment) =>
+      typeof attachment === 'string' ? attachment.length > 0 : attachment.filePath
+    )
+}
+
+/** 合并三个附件 ref 成统一 attachments 数组,带 type 区分来源 */
+const getAttachments = () => [
+  ...normalizeAttachments(applicationFile.value, 'APPLICATION'),
+  ...normalizeAttachments(openingReportFile.value, 'OPENING_REPORT'),
+  ...normalizeAttachments(otherFile.value, 'OTHER')
+]
+
+/** 清空附件 */
+const resetAttachments = () => {
+  applicationFile.value = []
+  openingReportFile.value = []
+  otherFile.value = []
+}
+
 // ========== 列表与查询 ==========
 const list = ref<any[]>([])
 const total = ref(0)
@@ -512,19 +560,19 @@ const resetQuery = () => {
 
 // ========== 状态辅助方法 ==========
 const getPreAuditLabel = (status: number) => {
-  const map = { 0: '未提交', 1: '审批中', 2: '已通过', 3: '已驳回' }
+  const map: Record<number, string> = { 0: '未提交', 1: '审批中', 2: '已通过', 3: '已驳回' }
   return map[status] || '未知'
 }
 const getPreAuditTagType = (status: number) => {
-  const map = { 0: 'info', 1: 'warning', 2: 'success', 3: 'danger' }
+  const map: Record<number, string> = { 0: 'info', 1: 'warning', 2: 'success', 3: 'danger' }
   return map[status] || 'info'
 }
 const getFormalAuditLabel = (status: number) => {
-  const map = { 0: '未提交', 1: '审批中', 2: '已通过', 3: '已驳回' }
+  const map: Record<number, string> = { 0: '未提交', 1: '审批中', 2: '已通过', 3: '已驳回' }
   return map[status] || '未知'
 }
 const getFormalAuditTagType = (status: number) => {
-  const map = { 0: 'info', 1: 'warning', 2: 'success', 3: 'danger' }
+  const map: Record<number, string> = { 0: 'info', 1: 'warning', 2: 'success', 3: 'danger' }
   return map[status] || 'info'
 }
 
@@ -533,8 +581,7 @@ const canEdit = (row: any) => row.preAuditStatus === 0 || row.preAuditStatus ===
 const canSubmitPre = (row: any) => row.preAuditStatus === 0 || row.preAuditStatus === 3
 const canSubmitFormal = (row: any) => row.preAuditStatus === 2 && row.formalAuditStatus === 0
 const canApprove = (row: any) => {
-  // 这里简化处理,具体以后端返回的当前审批人是不是自己判断
-  // 实际项目中应该拿 row.preCurrentApproverId / row.formalCurrentApproverId 与当前用户比对
+  // 简化处理,实际项目应比对当前审批人是否是自己
   return row.preAuditStatus === 1 || row.formalAuditStatus === 1
 }
 
@@ -553,6 +600,7 @@ const createBudgetDetail = (): BudgetDetail => ({
   budgetDescription: ''
 })
 
+// formData 中【不包含】任何附件字段
 const createForm = () => ({
   deptId: null as number | null,
   projectLevel: '',
@@ -564,9 +612,6 @@ const createForm = () => ({
   goalAndSignificance: '',
   mainResearchContent: '',
   innovationPoint: '',
-  applicationFile: '',
-  openingReportFile: '',
-  otherFile: '',
   status: 0,
   remark: '',
   involveBudget: 0,
@@ -600,14 +645,29 @@ const openForm = async (project?: any) => {
   try {
     if (project?.id) {
       const data = await getProject(project.id)
+
+      // 关键:先把三个附件字段从 data 中摘出来,避免 ...data 污染 formData
+      const {
+        applicationFile: applicationFileData,
+        openingReportFile: openingReportFileData,
+        otherFile: otherFileData,
+        ...restData
+      } = data
+
       formData.value = {
         ...createForm(),
-        ...data,
-        scheduleList: data.scheduleList?.length ? data.scheduleList : [createSchedule()],
-        budgetList: data.budgetList?.length ? data.budgetList : [createBudgetDetail()]
+        ...restData,
+        scheduleList: restData.scheduleList?.length ? restData.scheduleList : [createSchedule()],
+        budgetList: restData.budgetList?.length ? restData.budgetList : [createBudgetDetail()]
       }
+
+      // 附件回显到独立 ref(与 index2.vue 一致)
+      applicationFile.value = normalizeAttachments(applicationFileData, 'APPLICATION')
+      openingReportFile.value = normalizeAttachments(openingReportFileData, 'OPENING_REPORT')
+      otherFile.value = normalizeAttachments(otherFileData, 'OTHER')
     } else {
       formData.value = createForm()
+      resetAttachments()
     }
   } finally {
     formLoading.value = false
@@ -615,7 +675,7 @@ const openForm = async (project?: any) => {
 }
 
 const openDetail = async (row: any) => {
-  // 详情可以直接复用 openForm,或者单独开只读弹窗
+  // 详情复用 openForm
   openForm(row)
 }
 
@@ -644,7 +704,13 @@ const submitForm = async () => {
     formData.value.scheduleList.forEach((item, index) => (item.sort = index))
     formData.value.budgetList.forEach((item, index) => (item.sort = index))
 
-    const payload: any = { ...formData.value }
+    // 关键:payload 里 formData 不含附件字段;附件通过 attachments 统一提交
+    // 与 index2.vue 完全一致的写法
+    const payload: any = {
+      ...formData.value,
+      attachments: getAttachments()
+    }
+
     if (editingId.value) {
       payload.id = editingId.value
       await updateProject(payload)