lrj
1 天以前 f04f35b562760afbac0c477357e2a29f77aec3b9
web/src/views/review-list.vue
@@ -60,18 +60,24 @@
      v-loading="projectsLoading"
      empty-text="请先选择比赛"
    >
      <el-table-column prop="playerName" label="项目名称" min-width="150">
      <el-table-column prop="projectName" label="项目名称" min-width="150">
        <template #default="scope">
          {{ scope.row.projectName || scope.row.playerName }}
          {{ scope.row.projectName || '未填写项目名称' }}
        </template>
      </el-table-column>
      <el-table-column prop="playerName" label="参赛人姓名" min-width="120" />
      <el-table-column prop="phone" label="联系电话" min-width="120" />
      <el-table-column prop="ratingCount" label="评审次数" width="100" align="center">
        <template #default="scope">
          <el-tag :type="scope.row.ratingCount > 0 ? 'success' : 'info'">
          <el-button
            text
            :type="scope.row.ratingCount > 0 ? 'success' : 'info'"
            @click="showRatingList(scope.row)"
            :disabled="scope.row.ratingCount === 0"
            class="rating-count-btn"
          >
            {{ scope.row.ratingCount }}
          </el-tag>
          </el-button>
        </template>
      </el-table-column>
      <el-table-column prop="averageScore" label="平均分" width="100" align="center">
@@ -119,6 +125,107 @@
        @current-change="handleCurrentChange"
      />
    </div>
    <!-- 评审列表弹窗 -->
    <el-dialog
      v-model="ratingListVisible"
      title="评审列表"
      width="60%"
      :before-close="handleRatingListClose"
    >
      <div v-if="selectedProject">
        <div class="dialog-header">
          <h4>{{ selectedProject.projectName || selectedProject.playerName }} - 评审详情</h4>
          <p class="project-info">参赛人:{{ selectedProject.playerName }} | 联系电话:{{ selectedProject.phone }}</p>
        </div>
        <el-table
          :data="judgeRatings"
          v-loading="ratingsLoading"
          style="width: 100%"
        >
          <el-table-column prop="judgeName" label="评委姓名" min-width="120" />
          <el-table-column prop="totalScore" label="评分" width="100" align="center">
            <template #default="scope">
              <span v-if="scope.row.hasRated && scope.row.totalScore" class="score">
                {{ scope.row.totalScore.toFixed(1) }}
              </span>
              <span v-else class="no-score">未评分</span>
            </template>
          </el-table-column>
          <el-table-column prop="hasRated" label="状态" width="100" align="center">
            <template #default="scope">
              <el-tag :type="scope.row.hasRated ? 'success' : 'info'">
                {{ scope.row.hasRated ? '已评分' : '未评分' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="100" align="center">
            <template #default="scope">
              <el-button
                text
                type="primary"
                @click="viewRatingDetail(scope.row)"
                :disabled="!scope.row.hasRated"
                size="small"
              >
                查看详情
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="handleRatingListClose">关闭</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 评分详情弹窗 -->
    <el-dialog
      v-model="ratingDetailVisible"
      title="评分详情"
      width="50%"
      :before-close="handleRatingDetailClose"
    >
      <div v-if="selectedRating">
        <div class="rating-detail-header">
          <h4>评委:{{ selectedRating.judgeName }}</h4>
          <p>总分:<span class="total-score">{{ selectedRating.totalScore.toFixed(1) }}</span></p>
        </div>
        <el-table
          :data="ratingItems"
          v-loading="ratingDetailLoading"
          style="width: 100%"
        >
          <el-table-column prop="itemName" label="评分项目" min-width="150" />
          <el-table-column prop="score" label="得分" width="100" align="center">
            <template #default="scope">
              <span class="item-score">{{ scope.row.score }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="maxScore" label="满分" width="100" align="center">
            <template #default="scope">
              <span class="max-score">{{ scope.row.maxScore || 100 }}</span>
            </template>
          </el-table-column>
        </el-table>
        <div v-if="selectedRating.remark" class="rating-comment">
          <h5>评语:</h5>
          <p>{{ selectedRating.remark }}</p>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="handleRatingDetailClose">关闭</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
@@ -127,8 +234,10 @@
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { Search, Trophy, View } from '@element-plus/icons-vue'
import { getActiveActivities, getRatingStats } from '@/api/projectReview'
import { getActiveActivities, getRatingStats, getJudgeRatingDetail } from '@/api/projectReview'
import { getProjectReviewApplications } from '@/api/projectReviewNew'
import { userApi } from '@/api/user'
import { getUserInfo } from '@/utils/auth'
const router = useRouter()
@@ -139,6 +248,16 @@
const searchName = ref('')
const activitiesLoading = ref(false)
const projectsLoading = ref(false)
// 评审弹窗相关数据
const ratingListVisible = ref(false)
const ratingDetailVisible = ref(false)
const selectedProject = ref(null)
const selectedRating = ref(null)
const judgeRatings = ref([])
const ratingItems = ref([])
const ratingsLoading = ref(false)
const ratingDetailLoading = ref(false)
// 分页数据
const currentPage = ref(1)
@@ -251,8 +370,147 @@
}
// 查看详情
const viewDetails = (projectId) => {
  router.push(`/project-review/${projectId}/detail`)
const viewDetails = async (projectId) => {
  // 传递stageId参数,selectedActivity.value就是当前选中的stageId
  const stageId = selectedActivity.value
  if (!stageId) {
    ElMessage.warning('请先选择比赛阶段')
    return
  }
  try {
    // 获取当前用户信息
    const userInfo = getUserInfo()
    if (!userInfo) {
      ElMessage.error('用户未登录,请重新登录')
      return
    }
    // 检查用户是否有employee身份
    const hasEmployeeRole = !!userInfo.employee
    if (hasEmployeeRole) {
      // 如果用户有employee身份,直接允许查看(不需要权限检查)
      console.log('用户具有员工身份,允许查看所有评分记录')
      router.push(`/project-review/${projectId}/detail?stageId=${stageId}`)
      return
    }
    // 如果用户只有judge身份(没有employee身份),需要检查评委权限
    const judgeInfo = await userApi.getCurrentJudgeInfo()
    if (!judgeInfo) {
      ElMessage.error('您没有评委权限,无法进行评审')
      return
    }
    // 检查评委是否有当前比赛阶段的权限
    const hasPermission = await userApi.checkJudgeInActivity(stageId, judgeInfo.judgeId)
    if (!hasPermission) {
      ElMessage.error('您没有当前比赛阶段的评审权限,无法进入评审页面')
      return
    }
    // 权限检查通过,跳转到评审页面
    router.push(`/project-review/${projectId}/detail?stageId=${stageId}`)
  } catch (error) {
    console.error('权限检查失败:', error)
    ElMessage.error('权限验证失败,请重新登录')
  }
}
// 显示评审列表
const showRatingList = async (project) => {
  if (project.ratingCount === 0) {
    ElMessage.warning('该项目暂无评审记录')
    return
  }
  selectedProject.value = project
  ratingListVisible.value = true
  // 加载评审列表
  await loadJudgeRatings(project.id)
}
// 加载评委评分列表
const loadJudgeRatings = async (activityPlayerId) => {
  ratingsLoading.value = true
  try {
    const result = await getRatingStats(activityPlayerId)
    console.log('getRatingStats 返回数据:', result)
    // 使用正确的字段名
    judgeRatings.value = result.ratings || []
    console.log('judgeRatings 设置为:', judgeRatings.value)
  } catch (error) {
    console.error('加载评审列表失败:', error)
    ElMessage.error('加载评审列表失败')
    judgeRatings.value = []
  } finally {
    ratingsLoading.value = false
  }
}
// 查看评分详情
const viewRatingDetail = async (rating) => {
  if (!rating.hasRated) {
    ElMessage.warning('该评委尚未评分')
    return
  }
  selectedRating.value = rating
  ratingDetailVisible.value = true
  // 加载评分明细
  await loadRatingDetail(selectedProject.value.id, rating.judgeId)
}
// 加载评分明细
const loadRatingDetail = async (activityPlayerId, judgeId) => {
  ratingDetailLoading.value = true
  try {
    console.log('加载评分明细,activityPlayerId:', activityPlayerId, 'judgeId:', judgeId)
    const result = await getJudgeRatingDetail(activityPlayerId, judgeId)
    console.log('评分明细API返回:', result)
    if (result && result.items) {
      ratingItems.value = result.items.map(item => ({
        itemName: item.ratingItemName,
        score: item.score,
        maxScore: item.maxScore || 100
      }))
      // 更新选中评分的备注信息
      if (selectedRating.value) {
        selectedRating.value.remark = result.remark
      }
    } else {
      ratingItems.value = []
    }
  } catch (error) {
    console.error('加载评分明细失败:', error)
    ElMessage.error('加载评分明细失败: ' + (error.message || '未知错误'))
    ratingItems.value = []
  } finally {
    ratingDetailLoading.value = false
  }
}
// 关闭评审列表弹窗
const handleRatingListClose = () => {
  ratingListVisible.value = false
  selectedProject.value = null
  judgeRatings.value = []
}
// 关闭评分详情弹窗
const handleRatingDetailClose = () => {
  ratingDetailVisible.value = false
  selectedRating.value = null
  ratingItems.value = []
}
// 格式化日期
@@ -350,6 +608,84 @@
  line-height: 1.5;
}
/* 评审次数按钮样式 */
.rating-count-btn {
  font-weight: 500;
}
/* 弹窗样式 */
.dialog-header {
  margin-bottom: 20px;
  h4 {
    margin: 0 0 8px 0;
    color: #303133;
    font-size: 16px;
    font-weight: 600;
  }
  .project-info {
    margin: 0;
    color: #606266;
    font-size: 14px;
  }
}
.rating-detail-header {
  margin-bottom: 20px;
  h4 {
    margin: 0 0 8px 0;
    color: #303133;
    font-size: 16px;
    font-weight: 600;
  }
  .total-score {
    color: #409eff;
    font-weight: 600;
    font-size: 18px;
  }
}
.rating-comment {
  margin-top: 20px;
  padding: 16px;
  background-color: #f5f7fa;
  border-radius: 8px;
  h5 {
    margin: 0 0 8px 0;
    color: #303133;
    font-size: 14px;
    font-weight: 600;
  }
  p {
    margin: 0;
    color: #606266;
    line-height: 1.6;
  }
}
.score {
  color: #67c23a;
  font-weight: 600;
}
.no-score {
  color: #909399;
}
.item-score {
  color: #409eff;
  font-weight: 500;
}
.max-score {
  color: #909399;
}
/* 搜索工具栏 */
.search-toolbar {
  display: flex;