peng
2025-11-06 c4938f6f4e839890b032c75c7a57333a6a9157a9
wx/pages/judge/review.js
@@ -9,7 +9,7 @@
    
    // 提交作品信息
    submission: null,
    submissionId: '',
    activityPlayerId: '',
    
    // 活动信息
    activity: null,
@@ -53,7 +53,7 @@
  onLoad(options) {
    if (options.id) {
      this.setData({ submissionId: options.id })
      this.setData({ activityPlayerId: options.id })
      this.loadSubmissionDetail()
    }
  },
@@ -71,99 +71,148 @@
      this.setData({ loading: true })
      
      const query = `
        query GetSubmissionDetail($id: ID!) {
          submission(id: $id) {
        query GetActivityPlayerDetail($id: ID!) {
          activityPlayerDetail(id: $id) {
            id
            title
            projectName
            description
            files {
            activityName
            stageId
            state
            playerInfo {
              id
              name
              phone
              gender
              birthday
              education
              introduction
              userInfo {
                userId
                name
                phone
                avatarUrl
              }
            }
            regionInfo {
              id
              name
              fullPath
            }
            submissionFiles {
              id
              name
              url
              type
              size
              fullUrl
              fileExt
              fileSize
              mediaType
              thumbUrl
              fullThumbUrl
            }
            images
            videos
            submittedAt
            status
            participant {
              id
              name
              school
              major
              avatar
            }
            team {
              id
              name
              members {
            ratingForm {
              schemeId
              schemeName
              totalMaxScore
              items {
                id
                name
                role
              }
            }
            activity {
              id
              title
              description
              judgeCriteria {
                id
                name
                description
                maxScore
                weight
                orderNo
              }
            }
            myReview {
              id
              scores
              comment
              totalScore
              status
              reviewedAt
            }
          }
        }
      `
      
      const result = await graphqlRequest(query, { id: this.data.submissionId })
      const result = await graphqlRequest(query, { id: this.data.activityPlayerId })
      
      if (result && result.submission) {
        const submission = result.submission
      if (result && result.activityPlayerDetail) {
        const detail = result.activityPlayerDetail
        
        // 为每个文件添加下载状态
        if (submission.files) {
          submission.files = submission.files.map(file => ({
            ...file,
        // 构建submission对象以兼容现有的WXML模板
        const submission = {
          id: detail.id,
          title: detail.projectName,
          description: detail.description,
          files: detail.submissionFiles ? detail.submissionFiles.map(file => ({
            id: file.id,
            name: file.name,
            url: file.fullUrl || file.url,
            type: file.fileExt,
            size: file.fileSize,
            isDownloading: this.data.downloadingFiles.indexOf(file.id) > -1
          }))
          })) : [],
          images: detail.submissionFiles ? detail.submissionFiles
            .filter(file => file.mediaType === 1)
            .map(file => file.fullUrl || file.url) : [],
          videos: detail.submissionFiles ? detail.submissionFiles
            .filter(file => file.mediaType === 2)
            .map(file => file.fullUrl || file.url) : [],
          mediaList: (detail.submissionFiles || []).map(file => {
            const mt = file.mediaType
            let typeStr = ''
            if (mt === 1 || mt === 'image' || (typeof mt === 'string' && mt.startsWith('image'))) typeStr = 'image'
            else if (mt === 2 || mt === 'video' || (typeof mt === 'string' && mt.startsWith('video'))) typeStr = 'video'
            else if ((file.fileExt || '').toLowerCase().includes('pdf')) typeStr = 'pdf'
            else if ((file.fileExt || '').toLowerCase().includes('doc')) typeStr = 'word'
            else typeStr = 'file'
            return {
              id: file.id,
              name: file.name,
              size: file.fileSize,
              mediaType: typeStr,
              thumbUrl: file.fullThumbUrl || file.thumbUrl || file.fullUrl || file.url,
              url: file.fullUrl || file.url
            }
          }),
          participant: {
            id: detail.playerInfo.id,
            name: detail.playerInfo.name,
            phone: detail.playerInfo.phone || '',
            gender: this.getGenderText(detail.playerInfo.gender),
            birthday: detail.playerInfo.birthday || '',
            region: detail.regionInfo ? detail.regionInfo.name : '',
            education: detail.playerInfo.education || '',
            avatar: detail.playerInfo.userInfo?.avatarUrl || '/images/default-avatar.svg'
          },
          status: detail.state === 1 ? 'APPROVED' : detail.state === 2 ? 'REJECTED' : 'PENDING'
        }
        // 构建activity对象
        const activity = {
          id: detail.stageId,
          title: detail.activityName,
          description: detail.description,
          judgeCriteria: detail.ratingForm ? detail.ratingForm.items || [] : []
        }
        
        // 初始化评分数据
        const scores = {}
        let maxScore = 0
        
        if (submission.activity.judgeCriteria) {
          submission.activity.judgeCriteria.forEach(criterion => {
            scores[criterion.id] = submission.myReview ?
              submission.myReview.scores[criterion.id] || 0 : 0
        if (activity.judgeCriteria) {
          activity.judgeCriteria.forEach(criterion => {
            scores[criterion.id] = 0 // 暂时设为0,后续需要查询已有评分
            maxScore += criterion.maxScore
          })
        }
        
        this.setData({
          submission,
          activity: submission.activity,
          criteria: submission.activity.judgeCriteria || [],
          activity,
          criteria: activity.judgeCriteria || [],
          scores,
          maxScore,
          existingReview: submission.myReview,
          reviewStatus: submission.myReview ? submission.myReview.status : 'PENDING',
          comment: submission.myReview ? submission.myReview.comment : ''
          existingReview: null, // 暂时设为null,后续需要查询已有评分
          reviewStatus: 'PENDING',
          comment: ''
        })
        
        this.calculateTotalScore()
        // 检查是否已有评分
        this.checkReviewStatus()
      }
    } catch (error) {
      console.error('加载作品详情失败:', error)
@@ -180,30 +229,50 @@
  async checkReviewStatus() {
    try {
      const query = `
        query CheckReviewStatus($submissionId: ID!) {
          reviewStatus(submissionId: $submissionId) {
        query GetCurrentJudgeRating($activityPlayerId: ID!) {
          currentJudgeRating(activityPlayerId: $activityPlayerId) {
            id
            totalScore
            remark
            status
            canReview
            deadline
            ratedAt
            items {
              ratingItemId
              ratingItemName
              score
              weightedScore
            }
          }
        }
      `
      
      const result = await graphqlRequest(query, { submissionId: this.data.submissionId })
      const result = await graphqlRequest(query, { activityPlayerId: this.data.activityPlayerId })
      
      if (result && result.reviewStatus) {
        const { status, canReview, deadline } = result.reviewStatus
      if (result && result.currentJudgeRating) {
        const rating = result.currentJudgeRating
        
        if (!canReview) {
          wx.showModal({
            title: '无法评审',
            content: deadline ? `评审已截止(截止时间:${formatDate(deadline)})` : '当前无法进行评审',
            showCancel: false,
            success: () => {
              wx.navigateBack()
            }
        // 如果已有评分,填充数据
        const scores = {}
        let totalScore = 0
        if (rating.items) {
          rating.items.forEach(item => {
            scores[item.ratingItemId] = item.score
            totalScore += item.score
          })
        }
        this.setData({
          scores,
          totalScore,
          comment: rating.remark || '',
          existingReview: rating,
          reviewStatus: rating.status || 'COMPLETED'
        })
        console.log('已加载现有评分:', rating)
      } else {
        console.log('当前评委尚未评分')
      }
    } catch (error) {
      console.error('检查评审状态失败:', error)
@@ -229,7 +298,7 @@
    
    criteria.forEach(criterion => {
      const score = scores[criterion.id] || 0
      totalScore += score * (criterion.weight || 1)
      totalScore += score
    })
    
    this.setData({ totalScore })
@@ -242,20 +311,39 @@
    })
  },
  // 媒体点击
  // 媒体点击(通过 index 定位 mediaList 项)
  onMediaTap(e) {
    const { url, type } = e.currentTarget.dataset
    if (type === 'image') {
    const { index } = e.currentTarget.dataset
    const item = this.data.submission?.mediaList?.[index]
    if (!item) return
    if (item.mediaType === 'image') {
      const imgs = (this.data.submission.mediaList || [])
        .filter(it => it.mediaType === 'image')
        .map(it => it.url)
      wx.previewImage({
        current: url,
        urls: this.data.submission.images || []
        current: item.url,
        urls: imgs.length ? imgs : [item.url]
      })
    } else if (type === 'video') {
    } else if (item.mediaType === 'video') {
      this.setData({
        showMediaPreview: true,
        currentMedia: url,
        currentMedia: item.url,
        mediaType: 'video'
      })
    } else {
      wx.downloadFile({
        url: item.url,
        success: (res) => {
          if (res.statusCode === 200) {
            wx.openDocument({
              filePath: res.tempFilePath,
              showMenu: true
            })
          } else {
            wx.showToast({ title: '预览失败', icon: 'none' })
          }
        },
        fail: () => wx.showToast({ title: '下载失败', icon: 'none' })
      })
    }
  },
@@ -266,6 +354,44 @@
      showMediaPreview: false,
      currentMedia: null
    })
  },
  // 点击预览按钮:图片/视频用 wx.previewMedia,文档用 openDocument
  onPreviewTap(e) {
    const { index } = e.currentTarget.dataset
    const list = this.data.submission?.mediaList || []
    const item = list[index]
    if (!item) return
    if (item.mediaType === 'image' || item.mediaType === 'video') {
      const mediaList = list
        .filter(m => m.mediaType === 'image' || m.mediaType === 'video')
        .map(m => ({
          url: m.url,
          type: m.mediaType === 'video' ? 'video' : 'image',
          poster: m.thumbUrl || m.url
        }))
      const current = Math.max(0, mediaList.findIndex(m => m.url === item.url))
      wx.previewMedia({
        sources: mediaList,
        current
      })
    } else {
      wx.downloadFile({
        url: item.url,
        success: (res) => {
          if (res.statusCode === 200) {
            wx.openDocument({
              filePath: res.tempFilePath,
              showMenu: true
            })
          } else {
            wx.showToast({ title: '预览失败', icon: 'none' })
          }
        },
        fail: () => wx.showToast({ title: '下载失败', icon: 'none' })
      })
    }
  },
  // 下载文件
@@ -371,15 +497,7 @@
      })
      return false
    }
    if (comment.trim().length < 10) {
      wx.showToast({
        title: '评审意见至少10个字符',
        icon: 'error'
      })
      return false
    }
    return true
  },
@@ -388,34 +506,37 @@
    try {
      wx.showLoading({ title: '保存中...' })
      
      const { submissionId, scores, comment, totalScore } = this.data
      const { activityPlayerId, scores, comment, criteria, activity } = this.data
      // 构建评分项数组
      const ratings = criteria.map(criterion => ({
        itemId: parseInt(criterion.id),
        score: parseFloat(scores[criterion.id] || 0)
      }))
      
      const mutation = `
        mutation SaveReviewDraft($input: ReviewDraftInput!) {
          saveReviewDraft(input: $input) {
            success
            review {
              id
              status
            }
          }
        mutation SaveActivityPlayerRating($input: ActivityPlayerRatingInput!) {
          saveActivityPlayerRating(input: $input)
        }
      `
      
      const input = {
        submissionId,
        scores,
        comment: comment.trim(),
        totalScore
        activityPlayerId: parseInt(activityPlayerId),
        stageId: parseInt(activity.id),
        ratings,
        comment: comment.trim()
      }
      
      const result = await graphqlRequest(mutation, { input })
      
      if (result && result.saveReviewDraft.success) {
      if (result && result.saveActivityPlayerRating) {
        wx.showToast({
          title: '草稿已保存',
          icon: 'success'
        })
        // 重新加载评分状态
        await this.checkReviewStatus()
      }
    } catch (error) {
      console.error('保存草稿失败:', error)
@@ -451,41 +572,42 @@
      this.setData({ submitting: true })
      wx.showLoading({ title: '提交中...' })
      
      const { submissionId, scores, comment, totalScore } = this.data
      const { activityPlayerId, scores, comment, criteria, activity } = this.data
      // 构建评分项数组
      const ratings = criteria.map(criterion => ({
        itemId: criterion.id,
        score: scores[criterion.id] || 0
      }))
      
      const mutation = `
        mutation SubmitReview($input: ReviewSubmitInput!) {
          submitReview(input: $input) {
            success
            review {
              id
              status
              reviewedAt
            }
          }
        mutation SaveActivityPlayerRating($input: ActivityPlayerRatingInput!) {
          saveActivityPlayerRating(input: $input)
        }
      `
      
      const input = {
        submissionId,
        scores,
        comment: comment.trim(),
        totalScore
        activityPlayerId,
        stageId: activity.id,
        ratings,
        comment: comment.trim()
      }
      
      const result = await graphqlRequest(mutation, { input })
      
      if (result && result.submitReview.success) {
      if (result && result.saveActivityPlayerRating) {
        wx.showToast({
          title: '评审提交成功',
          title: '评分提交成功',
          icon: 'success'
        })
        
        // 更新状态
        this.setData({
          reviewStatus: 'COMPLETED',
          existingReview: result.submitReview.review
          reviewStatus: 'COMPLETED'
        })
        // 重新加载评分状态
        await this.checkReviewStatus()
        
        // 延迟返回上一页
        setTimeout(() => {
@@ -493,7 +615,7 @@
        }, 1500)
      }
    } catch (error) {
      console.error('提交评审失败:', error)
      console.error('提交评分失败:', error)
      wx.showToast({
        title: '提交失败',
        icon: 'error'
@@ -504,37 +626,24 @@
    }
  },
  // 查看其他评审
  // 查看其他评审 - 已隐藏功能
  /*
  onViewOtherReviews() {
    wx.navigateTo({
      url: `/pages/judge/reviews?submissionId=${this.data.submissionId}`
      url: `/pages/judge/reviews?activityPlayerId=${this.data.activityPlayerId}`
    })
  },
  */
  // 联系参赛者
  // 联系参赛者:直接拨打电话
  onContactParticipant() {
    const { submission } = this.data
    if (submission.participant) {
      wx.showActionSheet({
        itemList: ['发送消息', '查看详情'],
        success: (res) => {
          switch (res.tapIndex) {
            case 0:
              // 发送消息功能
              wx.navigateTo({
                url: `/pages/chat/chat?userId=${submission.participant.id}`
              })
              break
            case 1:
              // 查看用户详情
              wx.navigateTo({
                url: `/pages/user/profile?userId=${submission.participant.id}`
              })
              break
          }
        }
      })
    const phone =
      this.data.submission?.participant?.phone ||
      this.data.submission?.participant?.userInfo?.phone
    if (phone) {
      wx.makePhoneCall({ phoneNumber: String(phone) })
    } else {
      wx.showToast({ title: '无联系电话', icon: 'none' })
    }
  },
@@ -555,6 +664,13 @@
    }
  },
  // 性别转换函数
  getGenderText(gender) {
    if (gender === 0) return '男'
    if (gender === 1) return '女'
    return '未填写'
  },
  // 格式化日期
  formatDate(dateString) {
    return formatDate(dateString, 'YYYY-MM-DD HH:mm')
@@ -563,7 +679,7 @@
  // 分享页面
  onShareAppMessage() {
    return {
      title: '蓉易创 - 评审作品',
      title: '蓉e创 - 评审作品',
      path: '/pages/index/index'
    }
  }