Pārlūkot izejas kodu

fix: 修复项目管理前端字段名与后端不一致及部门选择器显示问题

- status→projectStatus、outsourceCost→externalCost、coopDeptId→collabDeptIds 对齐后端字段
- DeptSelector 修正 node-key、render-after-expand,支持多选折叠标签和搜索过滤
- ProjectCard/detail/stats/output 页面同步修正 projectStatus 字段引用
- 编辑项目时正确将 collaborations 映射为 collabDeptIds 回显

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
shenzx 1 mēnesi atpakaļ
vecāks
revīzija
664c2d6977

+ 1 - 1
src/api/performance/project.js

@@ -48,7 +48,7 @@ export function changeProjectStatus(projectId, status) {
   return request({
     url: '/performance/project/changeStatus',
     method: 'put',
-    data: { projectId, status }
+    data: { projectId, projectStatus: status }
   })
 }
 

+ 14 - 2
src/components/performance/DeptSelector.vue

@@ -4,14 +4,19 @@
       v-if="useTree"
       :model-value="modelValue"
       @update:model-value="handleChange"
-      :data="treeData"
+      :data="effectiveTreeData"
       :props="treeProps"
-      value-key="id"
+      node-key="id"
+      :render-after-expand="false"
       :placeholder="placeholder"
       :clearable="clearable"
       :multiple="multiple"
+      :show-checkbox="multiple"
+      :collapse-tags="multiple"
+      :collapse-tags-tooltip="multiple"
       :disabled="disabled"
       check-strictly
+      filterable
       :style="{ width: width }"
     />
     <el-select
@@ -92,6 +97,13 @@ const treeProps = {
   children: 'children'
 }
 
+const effectiveTreeData = computed(() => {
+  if (props.treeData && props.treeData.length > 0) {
+    return props.treeData
+  }
+  return defaultDeptList.map(d => ({ id: d.id, label: d.label }))
+})
+
 const flatOptions = computed(() => {
   if (props.treeData && props.treeData.length > 0) {
     // Flatten tree data for select mode

+ 1 - 1
src/components/performance/ProjectCard.vue

@@ -2,7 +2,7 @@
   <el-card shadow="hover" class="project-card" :class="{ 'project-card--clickable': clickable }" @click="handleClick">
     <div class="project-card__header">
       <h4 class="project-card__title">{{ project.projectName }}</h4>
-      <ReviewStatusTag :status="project.status" :options="statusOptions" />
+      <ReviewStatusTag :status="project.projectStatus" :options="statusOptions" />
     </div>
     <el-divider />
     <div class="project-card__body">

+ 1 - 1
src/views/performance/output/index.vue

@@ -37,7 +37,7 @@
           </el-table-column>
           <el-table-column label="项目状态" align="center" width="120">
             <template #default="scope">
-              <ReviewStatusTag :status="scope.row.status" :options="projectStatusOptions" />
+              <ReviewStatusTag :status="scope.row.projectStatus" :options="projectStatusOptions" />
             </template>
           </el-table-column>
           <el-table-column label="总产值得(万元)" align="center" prop="totalOutput" width="120" />

+ 2 - 2
src/views/performance/project/detail.vue

@@ -8,12 +8,12 @@
       </el-descriptions-item>
       <el-descriptions-item label="项目类型">{{ typeMap[project.projectType] || project.projectType }}</el-descriptions-item>
       <el-descriptions-item label="项目状态">
-        <ReviewStatusTag :status="project.status" :options="statusTagOptions" />
+        <ReviewStatusTag :status="project.projectStatus" :options="statusTagOptions" />
       </el-descriptions-item>
       <el-descriptions-item label="牵头部门">{{ project.deptName }}</el-descriptions-item>
       <el-descriptions-item label="协作部门">{{ project.coopDeptName || '无' }}</el-descriptions-item>
       <el-descriptions-item label="合同额(万元)">{{ project.contractAmount || '--' }}</el-descriptions-item>
-      <el-descriptions-item label="外协成本(万元)">{{ project.outsourceCost || '--' }}</el-descriptions-item>
+      <el-descriptions-item label="外协成本(万元)">{{ project.externalCost || '--' }}</el-descriptions-item>
       <el-descriptions-item label="预估产值(万元)">{{ project.estimatedOutput || '--' }}</el-descriptions-item>
       <el-descriptions-item label="实际产值(万元)">
         <span style="font-weight: bold; color: #409EFF; font-size: 16px">{{ project.actualOutput || '--' }}</span>

+ 33 - 27
src/views/performance/project/index.vue

@@ -10,8 +10,8 @@
           <el-option v-for="dict in projectTypeOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
         </el-select>
       </el-form-item>
-      <el-form-item label="项目状态" prop="status">
-        <el-select v-model="queryParams.status" placeholder="项目状态" clearable style="width: 200px">
+      <el-form-item label="项目状态" prop="projectStatus">
+        <el-select v-model="queryParams.projectStatus" placeholder="项目状态" clearable style="width: 200px">
           <el-option v-for="dict in projectStatusOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
         </el-select>
       </el-form-item>
@@ -79,28 +79,28 @@
       <el-table-column label="实际产值(万元)" align="center" prop="actualOutput" width="120" />
       <el-table-column label="项目状态" align="center" width="160">
         <template #default="scope">
-          <ReviewStatusTag :status="scope.row.status" :options="statusTagOptions" />
+          <ReviewStatusTag :status="scope.row.projectStatus" :options="statusTagOptions" />
         </template>
       </el-table-column>
       <el-table-column label="操作" align="center" width="240" class-name="small-padding fixed-width">
         <template #default="scope">
           <el-button link type="primary" icon="View" @click="handleDetail(scope.row)">详情</el-button>
-          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-if="scope.row.status !== 'COMPLETED'" v-hasPermi="['performance:project:edit']">编辑</el-button>
-          <el-dropdown @command="(cmd) => handleStatusChange(scope.row, cmd)" v-if="getAvailableTransitions(scope.row.status).length > 0">
+          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-if="scope.row.projectStatus !== 'COMPLETED'" v-hasPermi="['performance:project:edit']">编辑</el-button>
+          <el-dropdown @command="(cmd) => handleStatusChange(scope.row, cmd)" v-if="getAvailableTransitions(scope.row.projectStatus).length > 0">
             <el-button link type="warning" icon="Switch">
               状态<el-icon class="el-icon--right"><arrow-down /></el-icon>
             </el-button>
             <template #dropdown>
               <el-dropdown-menu>
                 <el-dropdown-item
-                  v-for="t in getAvailableTransitions(scope.row.status)"
+                  v-for="t in getAvailableTransitions(scope.row.projectStatus)"
                   :key="t.value"
                   :command="t.value"
                 >{{ t.label }}</el-dropdown-item>
               </el-dropdown-menu>
             </template>
           </el-dropdown>
-          <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-if="scope.row.status === 'PLANNING_NOT_STARTED'" v-hasPermi="['performance:project:remove']">删除</el-button>
+          <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-if="scope.row.projectStatus === 'PLANNING_NOT_STARTED'" v-hasPermi="['performance:project:remove']">删除</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -118,11 +118,11 @@
       >
         <template #actions>
           <el-button type="primary" size="small" @click.stop="handleUpdate(project)" v-hasPermi="['performance:project:edit']">编辑</el-button>
-          <el-dropdown @command="(cmd) => handleStatusChange(project, cmd)" v-if="getAvailableTransitions(project.status).length > 0">
+          <el-dropdown @command="(cmd) => handleStatusChange(project, cmd)" v-if="getAvailableTransitions(project.projectStatus).length > 0">
             <el-button type="warning" size="small" @click.stop>状态变更</el-button>
             <template #dropdown>
               <el-dropdown-menu>
-                <el-dropdown-item v-for="t in getAvailableTransitions(project.status)" :key="t.value" :command="t.value">{{ t.label }}</el-dropdown-item>
+                <el-dropdown-item v-for="t in getAvailableTransitions(project.projectStatus)" :key="t.value" :command="t.value">{{ t.label }}</el-dropdown-item>
               </el-dropdown-menu>
             </template>
           </el-dropdown>
@@ -156,8 +156,8 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="协作部门" prop="coopDeptId">
-              <DeptSelector v-model="form.coopDeptId" :useTree="true" :treeData="deptOptions" placeholder="请选择协作部门(可选)" />
+            <el-form-item label="协作部门" prop="collabDeptIds">
+              <DeptSelector v-model="form.collabDeptIds" :useTree="true" :treeData="deptOptions" :multiple="true" placeholder="请选择协作部门(可选)" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -170,8 +170,8 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="外协成本(万元)" prop="outsourceCost">
-              <el-input-number v-model="form.outsourceCost" :precision="4" :min="0" placeholder="请输入外协成本" style="width: 100%" />
+            <el-form-item label="外协成本(万元)" prop="externalCost">
+              <el-input-number v-model="form.externalCost" :precision="4" :min="0" placeholder="请输入外协成本" style="width: 100%" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -215,8 +215,8 @@
         </el-row>
         <el-row>
           <el-col :span="12">
-            <el-form-item label="项目状态" prop="status">
-              <el-select v-model="form.status" placeholder="请选择项目状态" style="width: 100%">
+            <el-form-item label="项目状态" prop="projectStatus">
+              <el-select v-model="form.projectStatus" placeholder="请选择项目状态" style="width: 100%">
                 <el-option v-for="dict in editableStatusOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
@@ -334,7 +334,7 @@ const columns = ref({
   contractAmount: { label: '合同额', visible: true },
   estimatedOutput: { label: '预估产值', visible: true },
   actualOutput: { label: '实际产值', visible: true },
-  status: { label: '项目状态', visible: true }
+  projectStatus: { label: '项目状态', visible: true }
 })
 
 const data = reactive({
@@ -343,13 +343,13 @@ const data = reactive({
     projectName: undefined,
     projectType: undefined,
     leadDeptId: undefined,
-    coopDeptId: undefined,
+    collabDeptIds: [],
     contractAmount: undefined,
-    outsourceCost: undefined,
+    externalCost: undefined,
     estimatedOutput: undefined,
     riskLevel: undefined,
     successRate: undefined,
-    status: undefined,
+    projectStatus: undefined,
     remark: undefined
   },
   queryParams: {
@@ -357,14 +357,14 @@ const data = reactive({
     pageSize: 10,
     projectName: undefined,
     projectType: undefined,
-    status: undefined,
+    projectStatus: undefined,
     deptId: undefined
   },
   rules: {
     projectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
     leadDeptId: [{ required: true, message: '牵头部门不能为空', trigger: 'change' }],
     projectType: [{ required: true, message: '项目类型不能为空', trigger: 'change' }],
-    status: [{ required: true, message: '项目状态不能为空', trigger: 'change' }]
+    projectStatus: [{ required: true, message: '项目状态不能为空', trigger: 'change' }]
   }
 })
 
@@ -373,7 +373,7 @@ const { queryParams, form, rules } = toRefs(data)
 const hasContract = computed(() => (form.value.contractAmount || 0) > 0)
 const computedActualOutput = computed(() => {
   const contract = form.value.contractAmount || 0
-  const cost = form.value.outsourceCost || 0
+  const cost = form.value.externalCost || 0
   return Math.max(0, contract - cost).toFixed(4)
 })
 const computedRiskOutput = computed(() => {
@@ -427,10 +427,10 @@ function handleSelectionChange(selection) {
 function reset() {
   form.value = {
     projectId: undefined, projectName: undefined, projectType: undefined,
-    leadDeptId: undefined, coopDeptId: undefined,
-    contractAmount: undefined, outsourceCost: undefined,
+    leadDeptId: undefined, collabDeptIds: [],
+    contractAmount: undefined, externalCost: undefined,
     estimatedOutput: undefined, riskLevel: undefined, successRate: undefined,
-    status: undefined, remark: undefined
+    projectStatus: undefined, remark: undefined
   }
   proxy.resetForm('projectRef')
 }
@@ -462,7 +462,13 @@ function handleUpdate(row) {
     return
   }
   getProject(projectId).then(res => {
-    form.value = res.data
+    const data = res.data
+    if (data.collaborations && data.collaborations.length > 0) {
+      data.collabDeptIds = data.collaborations.map(c => c.deptId)
+    } else {
+      data.collabDeptIds = []
+    }
+    form.value = data
     open.value = true
     title.value = '修改项目'
   })
@@ -487,7 +493,7 @@ function handleDelete(row) {
 }
 
 function handleStatusChange(row, newStatus) {
-  const transition = getAvailableTransitions(row.status).find(t => t.value === newStatus)
+  const transition = getAvailableTransitions(row.projectStatus).find(t => t.value === newStatus)
   const label = transition ? transition.label : newStatus
   proxy.$modal.confirm(`确认将项目「${row.projectName}」的状态${label}?`).then(() => {
     return changeProjectStatus(row.projectId, newStatus)

+ 1 - 1
src/views/performance/project/stats.vue

@@ -102,7 +102,7 @@
       <el-table-column label="牵头部门" align="center" prop="deptName" width="140" />
       <el-table-column label="项目状态" align="center" width="140">
         <template #default="scope">
-          <ReviewStatusTag :status="scope.row.status" :options="statusTagOptions" />
+          <ReviewStatusTag :status="scope.row.projectStatus" :options="statusTagOptions" />
         </template>
       </el-table-column>
       <el-table-column label="合同额(万元)" align="center" prop="contractAmount" width="120" />