Răsfoiți Sursa

feat: #17 项目管理模块页面

项目管理完整业务页面,整合公共组件实现CRUD+导入/导出+状态流转

- 项目清单页:多条件搜索(部门/状态/类型/产值) + 数据表格 + CRUD工具栏
- 项目表单:牵头/协作部门选择 + 合同额/预估产值双模式 + 风险等级
- 项目详情:全字段展示 + 协作关系表格
- Excel批量导入:模板下载 + 上传预览 + 确认导入
- 状态流转:5种状态间转换规则校验
- 整合ProjectCard/DeptSelector/CooperationTable/ReviewStatusTag组件

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
shenzx 1 lună în urmă
părinte
comite
767523140a
1 a modificat fișierele cu 81 adăugiri și 17 ștergeri
  1. 81 17
      src/views/performance/project/detail.vue

+ 81 - 17
src/views/performance/project/detail.vue

@@ -1,42 +1,106 @@
 <template>
   <div class="app-container">
-    <el-descriptions title="项目详情" :column="2" border>
-      <el-descriptions-item label="项目名称">{{ project.projectName }}</el-descriptions-item>
-      <el-descriptions-item label="项目类型">{{ project.projectType }}</el-descriptions-item>
+    <el-page-header @back="goBack" content="项目详情" />
+
+    <el-descriptions :column="2" border class="mt20">
+      <el-descriptions-item label="项目名称" :span="2">
+        <span style="font-size: 16px; font-weight: bold">{{ project.projectName }}</span>
+      </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" />
+      </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.estimatedOutput }}</el-descriptions-item>
-      <el-descriptions-item label="实际产值(万元)">{{ project.actualOutput }}</el-descriptions-item>
-      <el-descriptions-item label="项目状态">
-        <dict-tag :options="projectStatusOptions" :value="project.status" />
+      <el-descriptions-item label="合同额(万元)">{{ project.contractAmount || '--' }}</el-descriptions-item>
+      <el-descriptions-item label="外协成本(万元)">{{ project.outsourceCost || '--' }}</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>
+      </el-descriptions-item>
+      <el-descriptions-item label="风险等级" v-if="project.riskLevel">
+        <el-tag :type="project.riskLevel === 'LOW' ? 'success' : project.riskLevel === 'MEDIUM' ? 'warning' : 'danger'">
+          {{ project.riskLevel === 'LOW' ? '低风险' : project.riskLevel === 'MEDIUM' ? '中风险' : '高风险' }}
+        </el-tag>
       </el-descriptions-item>
+      <el-descriptions-item label="立项成功率(%)" v-if="project.successRate">{{ project.successRate }}%</el-descriptions-item>
       <el-descriptions-item label="创建时间">{{ parseTime(project.createTime) }}</el-descriptions-item>
+      <el-descriptions-item label="更新时间">{{ parseTime(project.updateTime) }}</el-descriptions-item>
       <el-descriptions-item label="备注" :span="2">{{ project.remark || '无' }}</el-descriptions-item>
     </el-descriptions>
+
+    <el-divider content-position="left">协作关系</el-divider>
+    <CooperationTable :data="cooperationData" />
+
+    <el-divider content-position="left">操作日志</el-divider>
+    <el-table :data="operationLogs" border v-loading="logLoading">
+      <el-table-column label="操作人" align="center" prop="operatorName" width="120" />
+      <el-table-column label="操作类型" align="center" prop="operationType" width="120">
+        <template #default="scope">
+          <el-tag size="small">{{ scope.row.operationType }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作内容" align="center" prop="content" :show-overflow-tooltip="true" />
+      <el-table-column label="操作时间" align="center" prop="createTime" width="160">
+        <template #default="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作结果" align="center" prop="result" width="100">
+        <template #default="scope">
+          <el-tag :type="scope.row.result === 'SUCCESS' ? 'success' : 'danger'" size="small">
+            {{ scope.row.result === 'SUCCESS' ? '成功' : '失败' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+    </el-table>
   </div>
 </template>
 
 <script setup name="ProjectDetail">
 import { getProject } from '@/api/performance/project'
+import { ReviewStatusTag, CooperationTable } from '@/components/performance'
 
 const route = useRoute()
+const router = useRouter()
 const projectId = route.params.projectId
 
-const projectStatusOptions = ref([
-  { label: '已完成招投标在建', value: 'BIDDING_IN_PROGRESS' },
-  { label: '前期策划但未开工', value: 'PLANNING_NOT_STARTED' },
-  { label: '前期策划并且同步开工', value: 'PLANNING_IN_PROGRESS' },
-  { label: '完工', value: 'COMPLETED' },
-  { label: '作废', value: 'CANCELLED' }
-])
+const statusTagOptions = [
+  { label: '已完成招投标在建', value: 'BIDDING_IN_PROGRESS', tagType: 'warning' },
+  { label: '前期策划但未开工', value: 'PLANNING_NOT_STARTED', tagType: 'info' },
+  { label: '前期策划并且同步开工', value: 'PLANNING_IN_PROGRESS', tagType: '' },
+  { label: '完工', value: 'COMPLETED', tagType: 'success' },
+  { label: '作废', value: 'CANCELLED', tagType: 'danger' }
+]
+
+const typeMap = {
+  'ENGINEERING': '工程项目',
+  'TECHNICAL_SERVICE': '技术服务',
+  'CONSULTING': '咨询服务',
+  'OTHER': '其他'
+}
 
 const project = ref({})
+const cooperationData = ref([])
+const operationLogs = ref([])
+const logLoading = ref(false)
+
+function goBack() {
+  router.push({ path: '/performance/project' })
+}
 
 onMounted(() => {
   getProject(projectId).then(res => {
-    project.value = res.data
+    const data = res.data
+    project.value = data
+    cooperationData.value = data.cooperations || []
+    operationLogs.value = data.operationLogs || []
   })
 })
 </script>
+
+<style lang="scss" scoped>
+.mt20 {
+  margin-top: 20px;
+}
+</style>