gaoluyang
2025-09-24 2ba24b9d1265d02f2f3d1f828f0e0bfb8fa9125e
自动生成项目总结报
已添加1个文件
596 ■■■■■ 文件已修改
src/views/collaborativeApproval/reportGeneration/index.vue 596 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/reportGeneration/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,596 @@
<template>
  <div class="report-generation">
    <!-- é¡µé¢å¤´éƒ¨ -->
    <div class="page-header">
      <h2>项目总结报告生成</h2>
      <div class="header-actions">
        <el-button v-if="!reportGenerated" type="primary" @click="generateReport" :loading="generating">
          <el-icon><Document /></el-icon>
          ç”ŸæˆæŠ¥å‘Š
        </el-button>
        <el-button v-if="reportGenerated" type="primary" @click="resetConfig">
          <el-icon><Refresh /></el-icon>
          ç”Ÿæˆæ–°æŠ¥å‘Š
        </el-button>
        <el-button @click="exportReport" :disabled="!reportGenerated">
          <el-icon><Download /></el-icon>
          å¯¼å‡ºæŠ¥å‘Š
        </el-button>
      </div>
    </div>
    <!-- æŠ¥å‘Šé…ç½®åŒºåŸŸ -->
    <el-card class="config-card" v-if="!reportGenerated">
      <template #header>
        <span>报告配置</span>
      </template>
      <el-form :model="reportConfig" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="项目名称">
              <el-select v-model="reportConfig.projectId" placeholder="请选择项目">
                <el-option
                  v-for="project in projectList"
                  :key="project.id"
                  :label="project.name"
                  :value="project.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="报告周期">
              <el-date-picker
                v-model="reportConfig.dateRange"
                type="daterange"
                range-separator="至"
                start-placeholder="开始日期"
                end-placeholder="结束日期"
                format="YYYY-MM-DD"
                value-format="YYYY-MM-DD"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </el-card>
    <!-- æŠ¥å‘Šå†…容展示区域 -->
    <div v-if="reportGenerated" class="report-content">
      <!-- é¡¹ç›®åŸºæœ¬ä¿¡æ¯ -->
      <el-card class="report-section">
        <template #header>
          <span>项目基本信息</span>
        </template>
        <el-descriptions :column="2" border>
          <el-descriptions-item label="项目名称">{{ reportData.projectInfo.name }}</el-descriptions-item>
          <el-descriptions-item label="项目经理">{{ reportData.projectInfo.manager }}</el-descriptions-item>
          <el-descriptions-item label="开始时间">{{ reportData.projectInfo.startDate }}</el-descriptions-item>
          <el-descriptions-item label="结束时间">{{ reportData.projectInfo.endDate }}</el-descriptions-item>
          <el-descriptions-item label="项目状态">
            <el-tag :type="getStatusType(reportData.projectInfo.status)">
              {{ reportData.projectInfo.status }}
            </el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="总预算">{{ reportData.projectInfo.budget }}</el-descriptions-item>
        </el-descriptions>
      </el-card>
      <!-- ä»»åŠ¡å®ŒæˆçŽ‡ç»Ÿè®¡ -->
      <el-card class="report-section">
        <template #header>
          <span>任务完成率统计</span>
        </template>
        <el-row :gutter="20">
          <el-col :span="12">
            <div class="completion-stats">
              <div class="stat-item">
                <div class="stat-label">总任务数</div>
                <div class="stat-value">{{ reportData.taskStats.total }}</div>
              </div>
              <div class="stat-item">
                <div class="stat-label">已完成</div>
                <div class="stat-value completed">{{ reportData.taskStats.completed }}</div>
              </div>
              <div class="stat-item">
                <div class="stat-label">进行中</div>
                <div class="stat-value in-progress">{{ reportData.taskStats.inProgress }}</div>
              </div>
              <div class="stat-item">
                <div class="stat-label">未开始</div>
                <div class="stat-value pending">{{ reportData.taskStats.pending }}</div>
              </div>
            </div>
          </el-col>
          <el-col :span="12">
            <div class="completion-rate">
              <el-progress
                type="circle"
                :percentage="reportData.taskStats.completionRate"
                :width="150"
                :stroke-width="8"
              >
                <template #default="{ percentage }">
                  <span class="percentage-value">{{ percentage }}%</span>
                  <div class="percentage-label">完成率</div>
                </template>
              </el-progress>
            </div>
          </el-col>
        </el-row>
      </el-card>
      <!-- é—®é¢˜è®°å½•统计 -->
      <el-card class="report-section">
        <template #header>
          <span>问题记录统计</span>
        </template>
        <el-row :gutter="20">
          <el-col :span="8">
            <div class="issue-summary">
              <div class="summary-item">
                <div class="summary-label">总问题数</div>
                <div class="summary-value">{{ reportData.issueStats.total }}</div>
              </div>
              <div class="summary-item">
                <div class="summary-label">已解决</div>
                <div class="summary-value resolved">{{ reportData.issueStats.resolved }}</div>
              </div>
              <div class="summary-item">
                <div class="summary-label">待解决</div>
                <div class="summary-value pending">{{ reportData.issueStats.pending }}</div>
              </div>
            </div>
          </el-col>
          <el-col :span="16">
            <el-table :data="reportData.issueStats.topIssues" size="small">
              <el-table-column prop="title" label="主要问题" />
              <el-table-column prop="severity" label="严重程度" width="100">
                <template #default="{ row }">
                  <el-tag :type="getSeverityType(row.severity)" size="small">
                    {{ row.severity }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column prop="status" label="状态" width="80">
                <template #default="{ row }">
                  <el-tag :type="row.status === '已解决' ? 'success' : 'warning'" size="small">
                    {{ row.status }}
                  </el-tag>
                </template>
              </el-table-column>
            </el-table>
          </el-col>
        </el-row>
      </el-card>
      <!-- å»¶è¯¯åˆ†æž -->
      <el-card class="report-section">
        <template #header>
          <span>延误分析</span>
        </template>
        <el-row :gutter="20">
          <el-col :span="12">
            <div class="delay-stats">
              <div class="delay-item">
                <div class="delay-label">延误任务数</div>
                <div class="delay-value">{{ reportData.delayAnalysis.delayedTasks }}</div>
              </div>
              <div class="delay-item">
                <div class="delay-label">平均延误天数</div>
                <div class="delay-value">{{ reportData.delayAnalysis.avgDelayDays }}</div>
              </div>
              <div class="delay-item">
                <div class="delay-label">最大延误天数</div>
                <div class="delay-value">{{ reportData.delayAnalysis.maxDelayDays }}</div>
              </div>
            </div>
          </el-col>
          <el-col :span="12">
            <div class="delay-reasons">
              <h4>主要延误原因</h4>
              <ul>
                <li v-for="reason in reportData.delayAnalysis.reasons" :key="reason.reason">
                  {{ reason.reason }} ({{ reason.count }}次)
                </li>
              </ul>
            </div>
          </el-col>
        </el-row>
      </el-card>
      <!-- å›¢é˜Ÿç»©æ•ˆ -->
      <el-card class="report-section">
        <template #header>
          <span>团队绩效</span>
        </template>
        <el-table :data="reportData.teamPerformance" size="small">
          <el-table-column prop="name" label="成员姓名" />
          <el-table-column prop="completedTasks" label="完成任务数" />
          <el-table-column prop="completionRate" label="完成率" width="100">
            <template #default="{ row }">
              <el-progress :percentage="row.completionRate" :show-text="false" />
              <span style="margin-left: 10px;">{{ row.completionRate }}%</span>
            </template>
          </el-table-column>
          <el-table-column prop="performance" label="绩效评级" width="100">
            <template #default="{ row }">
              <el-tag :type="getPerformanceType(row.performance)">
                {{ row.performance }}
              </el-tag>
            </template>
          </el-table-column>
        </el-table>
      </el-card>
      <!-- æ€»ç»“与建议 -->
      <el-card class="report-section">
        <template #header>
          <span>总结与建议</span>
        </template>
        <div class="summary-content">
          <h4>项目总结</h4>
          <p>{{ reportData.summary.conclusion }}</p>
          <h4>改进建议</h4>
          <ul>
            <li v-for="suggestion in reportData.summary.suggestions" :key="suggestion">
              {{ suggestion }}
            </li>
          </ul>
        </div>
      </el-card>
    </div>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { Document, Download, Refresh } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
// å“åº”式数据
const generating = ref(false)
const reportGenerated = ref(false)
// æŠ¥å‘Šé…ç½®
const reportConfig = reactive({
  projectId: '',
  dateRange: []
})
// é¡¹ç›®åˆ—表
const projectList = ref([
  { id: 1, name: '产品库存管理系统' },
  { id: 2, name: '客户关系管理平台' },
  { id: 3, name: '财务管理系统升级' },
  { id: 4, name: '移动端应用开发' }
])
// æŠ¥å‘Šæ•°æ®
const reportData = ref({})
// æ¨¡æ‹ŸæŠ¥å‘Šæ•°æ®
const mockReportData = {
  projectInfo: {
    name: '产品库存管理系统',
    manager: '张三',
    startDate: '2024-01-01',
    endDate: '2024-03-31',
    status: '已完成',
    budget: 'Â¥500,000'
  },
  taskStats: {
    total: 45,
    completed: 38,
    inProgress: 5,
    pending: 2,
    completionRate: 84
  },
  issueStats: {
    total: 12,
    resolved: 9,
    pending: 3,
    topIssues: [
      { title: '数据库连接超时', severity: '高', status: '已解决' },
      { title: '前端页面响应慢', severity: '中', status: '已解决' },
      { title: '权限验证异常', severity: '高', status: '待解决' },
      { title: '报表导出功能缺失', severity: '中', status: '待解决' }
    ]
  },
  delayAnalysis: {
    delayedTasks: 8,
    avgDelayDays: 3.5,
    maxDelayDays: 12,
    reasons: [
      { reason: '需求变更', count: 3 },
      { reason: '技术难题', count: 2 },
      { reason: '资源不足', count: 2 },
      { reason: '外部依赖', count: 1 }
    ]
  },
  teamPerformance: [
    { name: '李四', completedTasks: 12, completionRate: 92, performance: '优秀' },
    { name: '王五', completedTasks: 10, completionRate: 85, performance: '良好' },
    { name: '赵六', completedTasks: 8, completionRate: 78, performance: '良好' },
    { name: '钱七', completedTasks: 8, completionRate: 72, performance: '一般' }
  ],
  summary: {
    conclusion: '本项目整体执行情况良好,任务完成率达到84%,团队协作效率较高。主要问题集中在技术实现和需求变更方面,通过及时沟通和技术攻关,大部分问题已得到解决。',
    suggestions: [
      '加强需求分析阶段的工作,减少后期需求变更',
      '建立技术难题预警机制,提前识别和解决技术风险',
      '优化团队资源配置,提高整体工作效率',
      '完善项目管理流程,加强过程监控'
    ]
  }
}
// ç”ŸæˆæŠ¥å‘Š
const generateReport = async () => {
  if (!reportConfig.projectId) {
    ElMessage.warning('请选择项目')
    return
  }
  generating.value = true
  // æ¨¡æ‹Ÿç”ŸæˆæŠ¥å‘Šçš„过程
  setTimeout(() => {
    reportData.value = mockReportData
    reportGenerated.value = true
    generating.value = false
    ElMessage.success('报告生成成功')
  }, 2000)
}
// å¯¼å‡ºæŠ¥å‘Š
const exportReport = () => {
  ElMessage.success('报告导出功能开发中...')
}
// é‡ç½®é…ç½®ï¼Œç”Ÿæˆæ–°æŠ¥å‘Š
const resetConfig = () => {
  reportGenerated.value = false
  reportData.value = {}
  // é‡ç½®é…ç½®ä¸ºé»˜è®¤å€¼
  reportConfig.projectId = 1
  const endDate = new Date()
  const startDate = new Date()
  startDate.setDate(startDate.getDate() - 30)
  reportConfig.dateRange = [
    startDate.toISOString().split('T')[0],
    endDate.toISOString().split('T')[0]
  ]
  ElMessage.success('已重置配置,可以生成新报告')
}
// èŽ·å–çŠ¶æ€ç±»åž‹
const getStatusType = (status) => {
  const statusMap = {
    '已完成': 'success',
    '进行中': 'warning',
    '未开始': 'info',
    '已暂停': 'danger'
  }
  return statusMap[status] || 'info'
}
// èŽ·å–ä¸¥é‡ç¨‹åº¦ç±»åž‹
const getSeverityType = (severity) => {
  const severityMap = {
    '高': 'danger',
    '中': 'warning',
    '低': 'success'
  }
  return severityMap[severity] || 'info'
}
// èŽ·å–ç»©æ•ˆç±»åž‹
const getPerformanceType = (performance) => {
  const performanceMap = {
    '优秀': 'success',
    '良好': 'primary',
    '一般': 'warning',
    '待改进': 'danger'
  }
  return performanceMap[performance] || 'info'
}
// ç»„件挂载时初始化
onMounted(() => {
  // è®¾ç½®é»˜è®¤é¡¹ç›®
  reportConfig.projectId = 1
  // è®¾ç½®é»˜è®¤æ—¥æœŸèŒƒå›´ï¼ˆæœ€è¿‘30天)
  const endDate = new Date()
  const startDate = new Date()
  startDate.setDate(startDate.getDate() - 30)
  reportConfig.dateRange = [
    startDate.toISOString().split('T')[0],
    endDate.toISOString().split('T')[0]
  ]
})
</script>
<style scoped>
.report-generation {
  padding: 20px;
}
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}
.page-header h2 {
  margin: 0;
  color: #303133;
}
.header-actions {
  display: flex;
  gap: 10px;
}
.config-card {
  margin-bottom: 20px;
}
.report-content {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.report-section {
  margin-bottom: 20px;
}
.completion-stats {
  display: flex;
  justify-content: space-around;
  padding: 20px 0;
}
.stat-item {
  text-align: center;
}
.stat-label {
  font-size: 14px;
  color: #909399;
  margin-bottom: 8px;
}
.stat-value {
  font-size: 24px;
  font-weight: bold;
  color: #303133;
}
.stat-value.completed {
  color: #67c23a;
}
.stat-value.in-progress {
  color: #e6a23c;
}
.stat-value.pending {
  color: #909399;
}
.completion-rate {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 200px;
}
.percentage-value {
  font-size: 20px;
  font-weight: bold;
  color: #409eff;
}
.percentage-label {
  font-size: 12px;
  color: #909399;
  margin-top: 4px;
}
.issue-summary {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 20px 0;
}
.summary-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.summary-label {
  font-size: 14px;
  color: #606266;
}
.summary-value {
  font-size: 18px;
  font-weight: bold;
  color: #303133;
}
.summary-value.resolved {
  color: #67c23a;
}
.summary-value.pending {
  color: #e6a23c;
}
.delay-stats {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 20px 0;
}
.delay-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.delay-label {
  font-size: 14px;
  color: #606266;
}
.delay-value {
  font-size: 18px;
  font-weight: bold;
  color: #e6a23c;
}
.delay-reasons h4 {
  margin: 0 0 15px 0;
  color: #303133;
}
.delay-reasons ul {
  margin: 0;
  padding-left: 20px;
}
.delay-reasons li {
  margin-bottom: 8px;
  color: #606266;
}
.summary-content h4 {
  margin: 0 0 10px 0;
  color: #303133;
}
.summary-content p {
  line-height: 1.6;
  color: #606266;
  margin-bottom: 20px;
}
.summary-content ul {
  margin: 0;
  padding-left: 20px;
}
.summary-content li {
  margin-bottom: 8px;
  color: #606266;
  line-height: 1.5;
}
</style>