// pages/judge/review.js
|
const app = getApp()
|
const { graphqlRequest, formatDate: formatDateUtil } = require('../../lib/utils')
|
|
Page({
|
data: {
|
loading: false,
|
submitting: false,
|
|
// 提交作品信息
|
submission: null,
|
activityPlayerId: '',
|
stageId: null,
|
submissionId: null,
|
|
// 活动信息
|
activity: null,
|
|
// 评审标准
|
criteria: [],
|
|
// 评分数据
|
scores: {},
|
|
// 评审意见
|
comment: '',
|
|
// 总分
|
totalScore: 0,
|
maxScore: 0,
|
|
// 评审状态
|
reviewStatus: 'PENDING', // PENDING, COMPLETED
|
|
// 已有评审记录
|
existingReview: null
|
},
|
|
onLoad(options) {
|
if (options.id) {
|
this.setData({ activityPlayerId: options.id })
|
this.loadSubmissionDetail()
|
}
|
},
|
|
onShow() {
|
// 页面显示时检查评审状态
|
if (this.data.submissionId) {
|
this.checkReviewStatus()
|
}
|
},
|
|
transformMediaFile(file) {
|
const url = file.fullUrl || file.url
|
const thumbUrl = file.fullThumbUrl || url
|
const ext = (file.fileExt || '').toLowerCase()
|
let mediaType = 'file'
|
|
if (file.mediaType === 1 || ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic'].includes(ext)) {
|
mediaType = 'image'
|
} else if (file.mediaType === 2 || ['mp4', 'mov', 'avi', 'wmv', 'mkv', 'webm', 'flv'].includes(ext)) {
|
mediaType = 'video'
|
} else if (ext === 'pdf') {
|
mediaType = 'pdf'
|
} else if (['doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'wps', 'txt', 'rtf'].includes(ext)) {
|
mediaType = 'word'
|
}
|
|
return {
|
id: file.id,
|
name: file.name,
|
url,
|
thumbUrl,
|
mediaType,
|
size: file.fileSize || 0
|
}
|
},
|
|
// 加载提交作品详情
|
async loadSubmissionDetail() {
|
try {
|
this.setData({ loading: true })
|
|
const query = `
|
query GetActivityPlayerDetail($id: ID!) {
|
activityPlayerDetail(id: $id) {
|
id
|
projectName
|
description
|
activityName
|
stageId
|
state
|
playerInfo {
|
id
|
name
|
phone
|
gender
|
birthday
|
education
|
introduction
|
userInfo {
|
userId
|
name
|
phone
|
avatarUrl
|
}
|
}
|
regionInfo {
|
id
|
name
|
fullPath
|
}
|
submissionFiles {
|
id
|
name
|
fullUrl
|
fullThumbUrl
|
fileExt
|
fileSize
|
mediaType
|
}
|
ratingForm {
|
schemeId
|
schemeName
|
totalMaxScore
|
items {
|
id
|
name
|
maxScore
|
orderNo
|
}
|
}
|
}
|
}
|
`
|
|
const result = await graphqlRequest(query, { id: this.data.activityPlayerId })
|
|
if (result && result.activityPlayerDetail) {
|
const detail = result.activityPlayerDetail
|
|
// 构建submission对象以兼容现有的WXML模板
|
const submission = {
|
id: detail.id,
|
title: detail.projectName,
|
description: detail.description,
|
submittedAt: detail.submitTime || null,
|
team: detail.team || null,
|
participant: {
|
id: detail.playerInfo?.id,
|
name: detail.playerInfo?.name,
|
phone: detail.playerInfo?.phone || detail.playerInfo?.userInfo?.phone || '',
|
avatar: detail.playerInfo?.userInfo?.avatarUrl || '/images/default-avatar.svg',
|
gender: this.getGenderLabel(detail.playerInfo?.gender),
|
birthday: this.getBirthdayText(detail.playerInfo?.birthday),
|
region: detail.regionInfo?.fullPath || detail.regionInfo?.name || '',
|
education: detail.playerInfo?.education || '',
|
school: detail.regionInfo ? detail.regionInfo.name : '',
|
major: detail.playerInfo?.education || ''
|
},
|
status: detail.state === 1 ? 'APPROVED' : detail.state === 2 ? 'REJECTED' : 'PENDING',
|
mediaList: (detail.submissionFiles || []).map(file => this.transformMediaFile(file))
|
}
|
|
const criteria = (detail.ratingForm?.items || []).map(item => {
|
const maxScore = item.maxScore || 0
|
return {
|
id: item.id,
|
name: item.name,
|
maxScore,
|
description: item.description || '暂无描述',
|
step: maxScore > 20 ? 1 : 0.5,
|
currentScore: 0
|
}
|
})
|
|
const scores = {}
|
criteria.forEach(criterion => {
|
scores[criterion.id] = 0
|
})
|
|
const maxScore = detail.ratingForm?.totalMaxScore || criteria.reduce((sum, item) => sum + (item.maxScore || 0), 0)
|
|
this.setData({
|
submission,
|
activity: {
|
id: detail.id,
|
stageId: detail.stageId,
|
ratingSchemeId: detail.ratingForm?.schemeId || null,
|
totalMaxScore: maxScore
|
},
|
stageId: detail.stageId || null,
|
submissionId: detail.id,
|
criteria,
|
scores,
|
maxScore,
|
totalScore: 0,
|
existingReview: null,
|
reviewStatus: 'PENDING',
|
comment: ''
|
})
|
|
this.calculateTotalScore()
|
|
// 检查是否已有评分
|
this.checkReviewStatus()
|
}
|
} catch (error) {
|
console.error('加载作品详情失败:', error)
|
wx.showToast({
|
title: '加载失败',
|
icon: 'error'
|
})
|
} finally {
|
this.setData({ loading: false })
|
}
|
},
|
|
// 检查评审状态
|
async checkReviewStatus() {
|
try {
|
const query = `
|
query GetCurrentJudgeRating($activityPlayerId: ID!) {
|
currentJudgeRating(activityPlayerId: $activityPlayerId) {
|
id
|
totalScore
|
remark
|
status
|
ratedAt
|
items {
|
ratingItemId
|
ratingItemName
|
score
|
maxScore
|
}
|
}
|
}
|
`
|
|
const result = await graphqlRequest(query, { activityPlayerId: this.data.activityPlayerId })
|
|
if (result && result.currentJudgeRating) {
|
const rating = result.currentJudgeRating
|
|
// 如果已有评分,填充数据
|
const scores = {}
|
let totalScore = 0
|
|
if (rating.items) {
|
rating.items.forEach(item => {
|
const numericScore = item.score !== undefined && item.score !== null ? Number(item.score) : 0
|
scores[item.ratingItemId] = numericScore
|
totalScore += numericScore
|
})
|
}
|
|
const updatedCriteria = this.data.criteria.map(criterion => {
|
const value = scores[criterion.id] !== undefined ? scores[criterion.id] : 0
|
return {
|
...criterion,
|
currentScore: value
|
}
|
})
|
|
const normalizedTotal = Number(totalScore.toFixed(2))
|
|
this.setData({
|
scores,
|
criteria: updatedCriteria,
|
totalScore: normalizedTotal,
|
comment: rating.remark || rating.comment || '',
|
existingReview: {
|
...rating,
|
totalScore: rating.totalScore ? Number(rating.totalScore) : normalizedTotal,
|
reviewedAt: rating.ratedAt || rating.reviewedAt || rating.updateTime || null
|
},
|
reviewStatus: 'COMPLETED'
|
})
|
|
console.log('已加载现有评分:', rating)
|
} else {
|
console.log('当前评委尚未评分')
|
}
|
|
this.calculateTotalScore()
|
} catch (error) {
|
console.error('检查评审状态失败:', error)
|
}
|
},
|
|
normalizeScore(value, criterion) {
|
const maxScore = Number(criterion.maxScore || 0)
|
const step = Number(criterion.step || (maxScore > 20 ? 1 : 0.5))
|
if (Number.isNaN(value)) {
|
value = 0
|
}
|
let normalized = Math.round(value / step) * step
|
if (normalized < 0) normalized = 0
|
if (normalized > maxScore) normalized = maxScore
|
return Number(normalized.toFixed(2))
|
},
|
|
updateCriterionScore(criterionId, index, value) {
|
const criterion = this.data.criteria[index]
|
if (!criterion) return
|
const normalized = this.normalizeScore(value, criterion)
|
|
this.setData({
|
[`scores.${criterionId}`]: normalized,
|
[`criteria[${index}].currentScore`]: normalized
|
})
|
|
this.calculateTotalScore()
|
},
|
|
// 评分改变
|
onScoreChange(e) {
|
const { criterionId, index } = e.currentTarget.dataset
|
const criterion = this.data.criteria[index]
|
if (!criterion) return
|
|
const inputValue = Number(e.detail.value)
|
const newScore = this.normalizeScore(inputValue, criterion)
|
this.updateCriterionScore(criterionId, index, newScore)
|
},
|
|
increaseScore(e) {
|
const { criterionId, index } = e.currentTarget.dataset
|
const criterion = this.data.criteria[index]
|
if (!criterion) return
|
const current = Number(this.data.scores[criterionId] || criterion.currentScore || 0)
|
const step = criterion.step || (criterion.maxScore > 20 ? 1 : 0.5)
|
this.updateCriterionScore(criterionId, index, current + step)
|
},
|
|
decreaseScore(e) {
|
const { criterionId, index } = e.currentTarget.dataset
|
const criterion = this.data.criteria[index]
|
if (!criterion) return
|
const current = Number(this.data.scores[criterionId] || criterion.currentScore || 0)
|
const step = criterion.step || (criterion.maxScore > 20 ? 1 : 0.5)
|
this.updateCriterionScore(criterionId, index, current - step)
|
},
|
|
// 计算总分
|
calculateTotalScore() {
|
const { scores, criteria } = this.data
|
let totalScore = 0
|
|
criteria.forEach(criterion => {
|
const score = Number(scores[criterion.id] || 0)
|
totalScore += score
|
})
|
|
this.setData({ totalScore: Number(totalScore.toFixed(2)) })
|
},
|
|
// 评审意见输入
|
onCommentInput(e) {
|
this.setData({
|
comment: e.detail.value
|
})
|
},
|
|
// 媒体点击
|
onMediaTap(e) {
|
const index = Number(e.currentTarget.dataset.index)
|
const mediaList = this.data.submission?.mediaList || []
|
const media = mediaList[index]
|
if (!media) return
|
|
if (media.mediaType === 'image') {
|
const imageUrls = mediaList
|
.filter(item => item.mediaType === 'image')
|
.map(item => item.url)
|
wx.previewImage({
|
current: media.url,
|
urls: imageUrls
|
})
|
} else if (media.mediaType === 'video') {
|
wx.navigateTo({
|
url: `/pages/video/video?url=${encodeURIComponent(media.url)}&title=${encodeURIComponent(media.name)}`
|
})
|
} else {
|
this.openDocumentMedia(media)
|
}
|
},
|
|
async openDocumentMedia(media) {
|
try {
|
wx.showLoading({ title: '打开中...' })
|
const downloadRes = await new Promise((resolve, reject) => {
|
wx.downloadFile({
|
url: media.url,
|
success: resolve,
|
fail: reject
|
})
|
})
|
|
if (downloadRes.statusCode !== 200) {
|
throw new Error('文件下载失败')
|
}
|
|
await new Promise((resolve, reject) => {
|
wx.openDocument({
|
filePath: downloadRes.tempFilePath,
|
showMenu: true,
|
success: resolve,
|
fail: reject
|
})
|
})
|
} catch (error) {
|
console.error('打开文件失败:', error)
|
wx.showToast({
|
title: '无法打开文件',
|
icon: 'error'
|
})
|
} finally {
|
wx.hideLoading()
|
}
|
},
|
|
// 验证评审数据
|
validateReview() {
|
const { scores, criteria, comment } = this.data
|
const commentText = (comment || '').trim()
|
|
// 检查是否所有标准都已评分
|
for (let criterion of criteria) {
|
if (!scores[criterion.id] || scores[criterion.id] === 0) {
|
wx.showToast({
|
title: `请为"${criterion.name}"评分`,
|
icon: 'error'
|
})
|
return false
|
}
|
}
|
|
// 检查评审意见
|
if (!commentText) {
|
wx.showToast({
|
title: '请填写评审意见',
|
icon: 'error'
|
})
|
return false
|
}
|
|
if (commentText.length < 10) {
|
wx.showToast({
|
title: '评审意见至少10个字符',
|
icon: 'error'
|
})
|
return false
|
}
|
|
return true
|
},
|
|
// 提交评审
|
async onSubmitReview() {
|
if (!this.validateReview()) {
|
return
|
}
|
|
wx.showModal({
|
title: '确认提交',
|
content: '评审提交后将无法修改,确定要提交吗?',
|
success: async (res) => {
|
if (res.confirm) {
|
await this.submitReview()
|
}
|
}
|
})
|
},
|
|
// 执行提交评审
|
async submitReview() {
|
try {
|
this.setData({ submitting: true })
|
wx.showLoading({ title: '提交中...' })
|
|
const { activityPlayerId, scores, comment, criteria, stageId } = this.data
|
const commentText = (comment || '').trim()
|
|
if (!stageId) {
|
wx.showToast({
|
title: '缺少阶段信息,无法提交',
|
icon: 'none'
|
})
|
this.setData({ submitting: false })
|
wx.hideLoading()
|
return
|
}
|
|
// 构建评分项数组
|
const ratings = criteria.map(criterion => ({
|
itemId: criterion.id,
|
score: Number(scores[criterion.id] || 0)
|
}))
|
|
const mutation = `
|
mutation SaveActivityPlayerRating($input: ActivityPlayerRatingInput!) {
|
saveActivityPlayerRating(input: $input)
|
}
|
`
|
|
const input = {
|
activityPlayerId,
|
stageId,
|
ratings,
|
comment: commentText
|
}
|
|
const result = await graphqlRequest(mutation, { input })
|
|
if (result && result.saveActivityPlayerRating) {
|
wx.showToast({
|
title: '评分提交成功',
|
icon: 'success'
|
})
|
|
// 更新状态
|
this.setData({
|
reviewStatus: 'COMPLETED'
|
})
|
|
// 重新加载评分状态
|
await this.checkReviewStatus()
|
|
// 延迟返回上一页
|
setTimeout(() => {
|
wx.navigateBack()
|
}, 1500)
|
}
|
} catch (error) {
|
console.error('提交评分失败:', error)
|
wx.showToast({
|
title: '提交失败',
|
icon: 'error'
|
})
|
} finally {
|
this.setData({ submitting: false })
|
wx.hideLoading()
|
}
|
},
|
|
// 查看其他评审
|
onViewOtherReviews() {
|
wx.navigateTo({
|
url: `/pages/judge/reviews?activityPlayerId=${this.data.activityPlayerId}`
|
})
|
},
|
|
// 联系参赛者
|
onContactParticipant() {
|
const { submission } = this.data
|
const phone = submission?.participant?.phone
|
if (phone) {
|
wx.makePhoneCall({
|
phoneNumber: phone
|
})
|
} else {
|
wx.showToast({
|
title: '暂无联系方式',
|
icon: 'none'
|
})
|
}
|
},
|
|
// 获取文件大小文本
|
getFileSizeText(size) {
|
if (size < 1024) {
|
return `${size}B`
|
} else if (size < 1024 * 1024) {
|
return `${(size / 1024).toFixed(1)}KB`
|
} else {
|
return `${(size / (1024 * 1024)).toFixed(1)}MB`
|
}
|
},
|
|
// 统一处理性别显示文本
|
getGenderLabel(gender) {
|
if (gender === null || gender === undefined || gender === '') {
|
return '未填写'
|
}
|
|
const normalized = String(gender).trim().toLowerCase()
|
|
if (normalized === '') {
|
return '未填写'
|
}
|
|
if (normalized === 'male' || normalized === 'm') {
|
return '男'
|
}
|
if (normalized === 'female' || normalized === 'f') {
|
return '女'
|
}
|
|
if (/^-?\d+$/.test(normalized)) {
|
const numeric = Number(normalized)
|
if (numeric === 1) return '男'
|
if (numeric === 0) return '女'
|
if (numeric === 2) return '女'
|
}
|
|
return gender === undefined || gender === null ? '未填写' : String(gender)
|
},
|
|
// 统一处理出生日期显示文本
|
getBirthdayText(dateString) {
|
if (!dateString) {
|
return '未填写'
|
}
|
const formatted = formatDateUtil(dateString, 'YYYY-MM-DD')
|
return formatted || '未填写'
|
},
|
|
// 格式化日期
|
formatDate(dateString) {
|
return formatDateUtil(dateString, 'YYYY-MM-DD HH:mm')
|
},
|
|
// 分享页面
|
onShareAppMessage() {
|
return {
|
title: '蓉易创 - 评审作品',
|
path: '/pages/index/index'
|
}
|
}
|
})
|