yanghao hace 1 mes
padre
commit
a5ffe48cc5
Se han modificado 2 ficheros con 351 adiciones y 21 borrados
  1. 146 0
      src/views/findings/copyright/data.js
  2. 205 21
      src/views/findings/copyright/index.vue

+ 146 - 0
src/views/findings/copyright/data.js

@@ -0,0 +1,146 @@
+;[
+  {
+    name: '发明人',
+    identifier: 'jointInventors',
+    dataType: 'text',
+    required: 0,
+    unit: '',
+    accessMode: 'rw',
+    defaultValue: '',
+    maxValue: '',
+    minValue: '0',
+    sort: 1,
+    actualValue: '张三',
+    dropdownList: null
+  },
+  {
+    name: '是否有出质',
+    identifier: 'pledged',
+    dataType: 'dropdown',
+    required: 0,
+    unit: '',
+    accessMode: 'rw',
+    defaultValue: '',
+    maxValue: '',
+    minValue: '',
+    sort: 1,
+    actualValue: '是',
+    dropdownList: [
+      {
+        name: '是',
+        value: '是'
+      },
+      {
+        name: '否',
+        value: '否'
+      }
+    ]
+  },
+  {
+    name: '是否自主申报',
+    identifier: 'selfDeclared',
+    dataType: 'dropdown',
+    required: 0,
+    unit: '',
+    accessMode: 'rw',
+    defaultValue: '',
+    maxValue: '',
+    minValue: '',
+    sort: 1,
+    actualValue: '否',
+    dropdownList: [
+      {
+        name: '是',
+        value: '是'
+      },
+      {
+        name: '否',
+        value: '否'
+      }
+    ]
+  },
+  {
+    name: '下次缴费截止日期',
+    identifier: 'nextPaymentDate',
+    dataType: 'date',
+    required: 0,
+    unit: '',
+    accessMode: 'rw',
+    defaultValue: '',
+    maxValue: '',
+    minValue: '0',
+    sort: 1,
+    actualValue: '1787383290000',
+    dropdownList: null
+  },
+  {
+    name: '最近缴费日期',
+    identifier: 'lastPaymentDate',
+    dataType: 'date',
+    required: 0,
+    unit: '',
+    accessMode: 'rw',
+    defaultValue: '',
+    maxValue: '',
+    minValue: '0',
+    sort: 1,
+    actualValue: '1784704890000',
+    dropdownList: null
+  },
+  {
+    name: '有效期限',
+    identifier: 'validUntil',
+    dataType: 'date',
+    required: 0,
+    unit: '',
+    accessMode: 'rw',
+    defaultValue: '',
+    maxValue: '',
+    minValue: '0',
+    sort: 1,
+    actualValue: '2100324090000',
+    dropdownList: null
+  },
+  {
+    name: '授权公告日',
+    identifier: 'grantDate',
+    dataType: 'date',
+    required: 0,
+    unit: '',
+    accessMode: 'rw',
+    defaultValue: '',
+    maxValue: '',
+    minValue: '0',
+    sort: 1,
+    actualValue: '1784704890000',
+    dropdownList: null
+  },
+  {
+    name: '申请日',
+    identifier: 'applicationDate',
+    dataType: 'date',
+    required: 0,
+    unit: '',
+    accessMode: 'rw',
+    defaultValue: '',
+    maxValue: '',
+    minValue: '0',
+    sort: 1,
+    actualValue: '1779434490000',
+    dropdownList: null
+  },
+  {
+    name: '专利号',
+    identifier: 'patentNo',
+    dataType: 'text',
+    required: 0,
+    unit: '',
+    accessMode: 'rw',
+    defaultValue: '',
+    maxValue: '',
+    minValue: '0',
+    sort: 1,
+    actualValue: 'YFZL20260823',
+    dropdownList: null
+  }
+]

+ 205 - 21
src/views/findings/copyright/index.vue

@@ -22,10 +22,20 @@
         class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 pt-4 flex items-center flex-wrap min-w-0"
       >
         <el-form-item label="成果名称" prop="achievementName">
-          <el-input v-model="queryParams.achievementName" placeholder="请输入成果名称" clearable style="width: 180px" />
+          <el-input
+            v-model="queryParams.achievementName"
+            placeholder="请输入成果名称"
+            clearable
+            style="width: 180px"
+          />
         </el-form-item>
         <el-form-item label="成果类型" prop="achievementType">
-          <el-select v-model="queryParams.achievementType" placeholder="请选择成果类型" clearable style="width: 150px">
+          <el-select
+            v-model="queryParams.achievementType"
+            placeholder="请选择成果类型"
+            clearable
+            style="width: 150px"
+          >
             <el-option
               v-for="dict in getStrDictOptions(DICT_TYPE.ACHIEVEMENT_TYPE)"
               :key="dict.value"
@@ -52,24 +62,46 @@
               <zm-table-column label="序号" width="70" align="center">
                 <template #default="scope">{{ scope.$index + 1 }}</template>
               </zm-table-column>
-              <zm-table-column label="成果名称" prop="achievementName" min-width="180" align="center" show-overflow-tooltip />
-              <zm-table-column label="成果类型" prop="achievementType" min-width="130" align="center">
+              <zm-table-column
+                label="成果名称"
+                prop="achievementName"
+                min-width="180"
+                align="center"
+                show-overflow-tooltip
+              />
+              <zm-table-column
+                label="成果类型"
+                prop="achievementType"
+                min-width="130"
+                align="center"
+              >
                 <template #default="scope">
                   <dict-tag :type="DICT_TYPE.ACHIEVEMENT_TYPE" :value="scope.row.achievementType" />
                 </template>
               </zm-table-column>
               <zm-table-column label="子类型" prop="subType" min-width="140" align="center">
                 <template #default="scope">
-                  <dict-tag :type="getSubTypeDict(scope.row.achievementType)" :value="scope.row.subType" />
+                  <dict-tag
+                    :type="getSubTypeDict(scope.row.achievementType)"
+                    :value="scope.row.subType"
+                  />
                 </template>
               </zm-table-column>
               <zm-table-column label="部门" prop="deptId" min-width="150" align="center">
                 <template #default="{ row }">{{ getDeptName(row.deptId) }}</template>
               </zm-table-column>
-              <zm-table-column label="概述" prop="summary" min-width="260" align="center" show-overflow-tooltip />
+              <zm-table-column
+                label="概述"
+                prop="summary"
+                min-width="260"
+                align="center"
+                show-overflow-tooltip
+              />
               <zm-table-column label="操作" align="center" fixed="right" width="150" action>
                 <template #default="scope">
-                  <el-button link type="primary" @click="openForm('update', scope.row.id)">编辑</el-button>
+                  <el-button link type="primary" @click="openForm('update', scope.row.id)"
+                    >编辑</el-button
+                  >
                   <el-button link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
                 </template>
               </zm-table-column>
@@ -78,32 +110,123 @@
         </el-auto-resizer>
       </div>
       <div class="h-8 mt-2 flex items-center justify-end">
-        <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" />
+        <Pagination
+          :total="total"
+          v-model:page="queryParams.pageNo"
+          v-model:limit="queryParams.pageSize"
+          @pagination="getList"
+        />
       </div>
     </div>
   </div>
 
-  <Dialog :title="dialogTitle" v-model="dialogVisible" width="680px" :close-on-click-modal="false" destroy-on-close>
-    <el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
+  <Dialog
+    :title="dialogTitle"
+    v-model="dialogVisible"
+    width="680px"
+    :close-on-click-modal="false"
+    destroy-on-close
+  >
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+      v-loading="formLoading"
+    >
       <el-form-item label="部门" prop="deptId">
-        <el-tree-select v-model="formData.deptId" :data="deptList" :props="defaultProps" node-key="id" filterable clearable placeholder="请选择部门" class="w-full" />
+        <el-tree-select
+          v-model="formData.deptId"
+          :data="deptList"
+          :props="defaultProps"
+          node-key="id"
+          filterable
+          clearable
+          placeholder="请选择部门"
+          class="w-full"
+        />
       </el-form-item>
       <el-form-item label="成果名称" prop="achievementName">
         <el-input v-model="formData.achievementName" placeholder="请输入成果名称" />
       </el-form-item>
       <el-form-item label="成果类型" prop="achievementType">
-        <el-select v-model="formData.achievementType" placeholder="请选择成果类型" clearable class="w-full" @change="handleAchievementTypeChange">
-          <el-option v-for="dict in getStrDictOptions(DICT_TYPE.ACHIEVEMENT_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value" />
+        <el-select
+          v-model="formData.achievementType"
+          placeholder="请选择成果类型"
+          clearable
+          class="w-full"
+          @change="handleAchievementTypeChange"
+        >
+          <el-option
+            v-for="dict in getStrDictOptions(DICT_TYPE.ACHIEVEMENT_TYPE)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
         </el-select>
       </el-form-item>
       <el-form-item label="子类型" prop="subType">
-        <el-select v-model="formData.subType" placeholder="请先选择成果类型" clearable class="w-full" :disabled="!formData.achievementType">
-          <el-option v-for="dict in subTypeOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
+        <el-select
+          v-model="formData.subType"
+          placeholder="请先选择成果类型"
+          clearable
+          class="w-full"
+          :disabled="!formData.achievementType"
+        >
+          <el-option
+            v-for="dict in subTypeOptions"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
         </el-select>
       </el-form-item>
       <el-form-item label="概述" prop="summary">
-        <el-input v-model="formData.summary" type="textarea" :rows="5" placeholder="请输入成果概述" />
+        <el-input
+          v-model="formData.summary"
+          type="textarea"
+          :rows="5"
+          placeholder="请输入成果概述"
+        />
       </el-form-item>
+      <el-divider v-if="formData.extAttributes.length" content-position="left">专属字段</el-divider>
+      <div v-loading="extendLoading">
+        <el-form-item
+          v-for="(attribute, index) in formData.extAttributes"
+          :key="attribute.identifier || index"
+          :label="attribute.name"
+          :prop="`extAttributes.${index}.actualValue`"
+          :rules="getAttributeRules(attribute)"
+        >
+          <el-select
+            v-if="attribute.dataType === 'dropdown'"
+            v-model="attribute.actualValue"
+            placeholder="请选择"
+            clearable
+            class="w-full"
+          >
+            <el-option
+              v-for="option in attribute.dropdownList || []"
+              :key="option.value"
+              :label="option.name"
+              :value="option.value"
+            />
+          </el-select>
+          <el-date-picker
+            v-else-if="attribute.dataType === 'date'"
+            v-model="attribute.actualValue"
+            type="date"
+            value-format="x"
+            placeholder="请选择日期"
+            class="w-full"
+          />
+          <el-input
+            v-else
+            v-model="attribute.actualValue"
+            :placeholder="`请输入${attribute.name}`"
+          />
+        </el-form-item>
+      </div>
     </el-form>
     <template #footer>
       <el-button type="primary" :loading="formLoading" @click="submitForm">确定</el-button>
@@ -114,7 +237,14 @@
 
 <script setup lang="ts">
 import { computed, onMounted, reactive, ref } from 'vue'
-import { createResult, deleteResult, getResultDetail, getResultList, updateResult } from '@/api/technology‌'
+import {
+  createResult,
+  deleteResult,
+  getResultDetail,
+  getResultList,
+  updateResult,
+  getResultExtendList
+} from '@/api/technology‌'
 import * as DeptApi from '@/api/system/dept'
 import { useUserStore } from '@/store/modules/user'
 import { defaultProps } from '@/utils/tree'
@@ -127,6 +257,7 @@ const deptId = useUserStore().getUser.deptId || rootDeptId
 const message = useMessage()
 const loading = ref(false)
 const formLoading = ref(false)
+const extendLoading = ref(false)
 const dialogVisible = ref(false)
 const dialogTitle = ref('')
 const formType = ref<'create' | 'update'>('create')
@@ -150,7 +281,8 @@ const createDefaultForm = () => ({
   achievementName: '',
   achievementType: '',
   subType: '',
-  summary: ''
+  summary: '',
+  extAttributes: [] as ExtendAttribute[]
 })
 
 const formData = ref(createDefaultForm())
@@ -161,6 +293,17 @@ const formRules = {
   subType: [{ required: true, message: '请选择子类型', trigger: 'change' }]
 }
 
+interface ExtendAttribute {
+  name: string
+  identifier: string
+  dataType: string
+  required?: number
+  unit?: string
+  defaultValue?: string
+  actualValue?: string | number | null
+  dropdownList?: Array<{ name: string; value: string | number }>
+}
+
 const getSubTypeDict = (achievementType: string | number) => {
   const dictMap: Record<string, string> = {
     '1': DICT_TYPE.PAPER_SUB_TYPE,
@@ -172,7 +315,9 @@ const getSubTypeDict = (achievementType: string | number) => {
 }
 
 const subTypeOptions = computed(() =>
-  formData.value.achievementType ? getStrDictOptions(getSubTypeDict(formData.value.achievementType)) : []
+  formData.value.achievementType
+    ? getStrDictOptions(getSubTypeDict(formData.value.achievementType))
+    : []
 )
 
 const getDeptName = (id?: number) => {
@@ -220,6 +365,37 @@ const resetQuery = () => {
 
 const handleAchievementTypeChange = () => {
   formData.value.subType = ''
+  formData.value.extAttributes = []
+  if (formData.value.achievementType) {
+    loadExtendAttributes(formData.value.achievementType)
+  }
+}
+
+const normalizeExtendAttributes = (data: unknown): ExtendAttribute[] => {
+  if (Array.isArray(data)) return data
+  if (data && typeof data === 'object' && Array.isArray((data as any).data)) {
+    return (data as any).data
+  }
+  return []
+}
+
+const loadExtendAttributes = async (achievementType: string | number) => {
+  extendLoading.value = true
+  try {
+    const data = await getResultExtendList(achievementType)
+    formData.value.extAttributes = normalizeExtendAttributes(data).map((attribute) => ({
+      ...attribute,
+      actualValue: attribute.actualValue ?? attribute.defaultValue ?? ''
+    }))
+  } finally {
+    extendLoading.value = false
+  }
+}
+
+const getAttributeRules = (attribute: ExtendAttribute) => {
+  return attribute.required
+    ? [{ required: true, message: `请输入${attribute.name}`, trigger: 'change' }]
+    : []
 }
 
 const openForm = async (type: 'create' | 'update', id?: number) => {
@@ -229,7 +405,15 @@ const openForm = async (type: 'create' | 'update', id?: number) => {
   if (type === 'update') {
     formLoading.value = true
     try {
-      formData.value = { ...createDefaultForm(), ...(await getResultDetail(id!)) }
+      const data = await getResultDetail(id!)
+      formData.value = {
+        ...createDefaultForm(),
+        ...data,
+        extAttributes: normalizeExtendAttributes(data.extAttributes).map((attribute) => ({
+          ...attribute,
+          actualValue: attribute.actualValue ?? attribute.defaultValue ?? ''
+        }))
+      }
     } finally {
       formLoading.value = false
     }
@@ -269,4 +453,4 @@ onMounted(async () => {
   getList()
   deptList.value = await DeptApi.getSimpleDeptList()
 })
-</script>
+</script>