From 0a48616045ddce1562584543a0e89e5144051fde Mon Sep 17 00:00:00 2001 From: Codex Assistant <codex@example.com> Date: 星期日, 05 十月 2025 14:52:44 +0800 Subject: [PATCH] 报名审核 --- wx/pages/judge/review.js | 655 ++++++++++++++++++++++++++++++++--------------------------- 1 files changed, 358 insertions(+), 297 deletions(-) diff --git a/wx/pages/judge/review.js b/wx/pages/judge/review.js index babef8a..e317235 100644 --- a/wx/pages/judge/review.js +++ b/wx/pages/judge/review.js @@ -1,6 +1,6 @@ // pages/judge/review.js const app = getApp() -const { graphqlRequest, formatDate } = require('../../lib/utils') +const { graphqlRequest, formatDate: formatDateUtil } = require('../../lib/utils') Page({ data: { @@ -9,14 +9,16 @@ // 鎻愪氦浣滃搧淇℃伅 submission: null, - submissionId: '', - + activityPlayerId: '', + stageId: null, + submissionId: null, + // 娲诲姩淇℃伅 activity: null, - + // 璇勫鏍囧噯 criteria: [], - + // 璇勫垎鏁版嵁 scores: {}, @@ -31,29 +33,12 @@ reviewStatus: 'PENDING', // PENDING, COMPLETED // 宸叉湁璇勫璁板綍 - existingReview: null, - - // 濯掍綋棰勮 - showMediaPreview: false, - currentMedia: null, - mediaType: 'image', - - // 鏂囦欢涓嬭浇 - downloadingFiles: [], - - // 璇勫垎绛夌骇 - scoreOptions: [ - { value: 1, label: '1鍒� - 寰堝樊' }, - { value: 2, label: '2鍒� - 杈冨樊' }, - { value: 3, label: '3鍒� - 涓�鑸�' }, - { value: 4, label: '4鍒� - 鑹ソ' }, - { value: 5, label: '5鍒� - 浼樼' } - ] + existingReview: null }, onLoad(options) { if (options.id) { - this.setData({ submissionId: options.id }) + this.setData({ activityPlayerId: options.id }) this.loadSubmissionDetail() } }, @@ -65,105 +50,160 @@ } }, + 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 GetSubmissionDetail($id: ID!) { - submission(id: $id) { + query GetActivityPlayerDetail($id: ID!) { + activityPlayerDetail(id: $id) { id - title + projectName description - files { + activityName + stageId + state + playerInfo { id name - url - type - size - } - images - videos - submittedAt - status - participant { - id - name - school - major - avatar - } - team { - id - name - members { - id + phone + gender + birthday + education + introduction + userInfo { + userId name - role + phone + avatarUrl } } - activity { + regionInfo { id - title - description - judgeCriteria { + name + fullPath + } + submissionFiles { + id + name + fullUrl + fullThumbUrl + fileExt + fileSize + mediaType + } + ratingForm { + schemeId + schemeName + totalMaxScore + items { 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, - isDownloading: this.data.downloadingFiles.indexOf(file.id) > -1 - })) + // 鏋勫缓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 = {} - let maxScore = 0 - - if (submission.activity.judgeCriteria) { - submission.activity.judgeCriteria.forEach(criterion => { - scores[criterion.id] = submission.myReview ? - submission.myReview.scores[criterion.id] || 0 : 0 - maxScore += criterion.maxScore - }) - } - + 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: submission.activity, - criteria: submission.activity.judgeCriteria || [], + activity: { + id: detail.id, + stageId: detail.stageId, + ratingSchemeId: detail.ratingForm?.schemeId || null, + totalMaxScore: maxScore + }, + stageId: detail.stageId || null, + submissionId: detail.id, + criteria, scores, maxScore, - existingReview: submission.myReview, - reviewStatus: submission.myReview ? submission.myReview.status : 'PENDING', - comment: submission.myReview ? submission.myReview.comment : '' + totalScore: 0, + existingReview: null, + reviewStatus: 'PENDING', + comment: '' }) - + this.calculateTotalScore() + + // 妫�鏌ユ槸鍚﹀凡鏈夎瘎鍒� + this.checkReviewStatus() } } catch (error) { console.error('鍔犺浇浣滃搧璇︽儏澶辫触:', error) @@ -180,46 +220,126 @@ 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 + maxScore + } } } ` - 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 => { + 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 } = e.currentTarget.dataset - const { value } = e.detail - - this.setData({ - [`scores.${criterionId}`]: parseInt(value) - }) - - this.calculateTotalScore() + 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) }, // 璁$畻鎬诲垎 @@ -228,11 +348,11 @@ let totalScore = 0 criteria.forEach(criterion => { - const score = scores[criterion.id] || 0 - totalScore += score * (criterion.weight || 1) + const score = Number(scores[criterion.id] || 0) + totalScore += score }) - this.setData({ totalScore }) + this.setData({ totalScore: Number(totalScore.toFixed(2)) }) }, // 璇勫鎰忚杈撳叆 @@ -244,106 +364,58 @@ // 濯掍綋鐐瑰嚮 onMediaTap(e) { - const { url, type } = e.currentTarget.dataset - - if (type === 'image') { + 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: url, - urls: this.data.submission.images || [] + current: media.url, + urls: imageUrls }) - } else if (type === 'video') { - this.setData({ - showMediaPreview: true, - currentMedia: url, - mediaType: 'video' + } else if (media.mediaType === 'video') { + wx.navigateTo({ + url: `/pages/video/video?url=${encodeURIComponent(media.url)}&title=${encodeURIComponent(media.name)}` }) + } else { + this.openDocumentMedia(media) } }, - // 鍏抽棴濯掍綋棰勮 - onCloseMediaPreview() { - this.setData({ - showMediaPreview: false, - currentMedia: null - }) - }, - - // 涓嬭浇鏂囦欢 - async onDownloadFile(e) { - const { fileId, fileName, fileUrl } = e.currentTarget.dataset - + async openDocumentMedia(media) { try { - // 娣诲姞鍒颁笅杞戒腑鍒楄〃 - const downloadingFiles = [...this.data.downloadingFiles, fileId] - - // 鍚屾椂鏇存柊鏂囦欢鐨刬sDownloading瀛楁 - const submission = { ...this.data.submission } - if (submission.files) { - submission.files = submission.files.map(file => ({ - ...file, - isDownloading: file.id === fileId ? true : file.isDownloading - })) - } - - this.setData({ - downloadingFiles, - submission + wx.showLoading({ title: '鎵撳紑涓�...' }) + const downloadRes = await new Promise((resolve, reject) => { + wx.downloadFile({ + url: media.url, + success: resolve, + fail: reject + }) }) - - wx.showLoading({ title: '涓嬭浇涓�...' }) - - const result = await wx.downloadFile({ - url: fileUrl, - success: (res) => { - if (res.statusCode === 200) { - // 淇濆瓨鍒扮浉鍐屾垨鏂囦欢 - wx.saveFile({ - tempFilePath: res.tempFilePath, - success: () => { - wx.showToast({ - title: '涓嬭浇鎴愬姛', - icon: 'success' - }) - }, - fail: () => { - wx.showToast({ - title: '淇濆瓨澶辫触', - icon: 'error' - }) - } - }) - } - }, - fail: () => { - wx.showToast({ - title: '涓嬭浇澶辫触', - icon: 'error' - }) - } + + 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) + console.error('鎵撳紑鏂囦欢澶辫触:', error) wx.showToast({ - title: '涓嬭浇澶辫触', + title: '鏃犳硶鎵撳紑鏂囦欢', icon: 'error' }) } finally { - // 浠庝笅杞戒腑鍒楄〃绉婚櫎 - const downloadingFiles = this.data.downloadingFiles.filter(id => id !== fileId) - - // 鍚屾椂鏇存柊鏂囦欢鐨刬sDownloading瀛楁 - const submission = { ...this.data.submission } - if (submission.files) { - submission.files = submission.files.map(file => ({ - ...file, - isDownloading: file.id === fileId ? false : file.isDownloading - })) - } - - this.setData({ - downloadingFiles, - submission - }) wx.hideLoading() } }, @@ -351,6 +423,7 @@ // 楠岃瘉璇勫鏁版嵁 validateReview() { const { scores, criteria, comment } = this.data + const commentText = (comment || '').trim() // 妫�鏌ユ槸鍚︽墍鏈夋爣鍑嗛兘宸茶瘎鍒� for (let criterion of criteria) { @@ -364,7 +437,7 @@ } // 妫�鏌ヨ瘎瀹℃剰瑙� - if (!comment.trim()) { + if (!commentText) { wx.showToast({ title: '璇峰~鍐欒瘎瀹℃剰瑙�', icon: 'error' @@ -372,7 +445,7 @@ return false } - if (comment.trim().length < 10) { + if (commentText.length < 10) { wx.showToast({ title: '璇勫鎰忚鑷冲皯10涓瓧绗�', icon: 'error' @@ -381,51 +454,6 @@ } return true - }, - - // 淇濆瓨鑽夌 - async onSaveDraft() { - try { - wx.showLoading({ title: '淇濆瓨涓�...' }) - - const { submissionId, scores, comment, totalScore } = this.data - - const mutation = ` - mutation SaveReviewDraft($input: ReviewDraftInput!) { - saveReviewDraft(input: $input) { - success - review { - id - status - } - } - } - ` - - const input = { - submissionId, - scores, - comment: comment.trim(), - totalScore - } - - const result = await graphqlRequest(mutation, { input }) - - if (result && result.saveReviewDraft.success) { - wx.showToast({ - title: '鑽夌宸蹭繚瀛�', - icon: 'success' - }) - } - } catch (error) { - console.error('淇濆瓨鑽夌澶辫触:', error) - wx.showToast({ - title: '淇濆瓨澶辫触', - icon: 'error' - }) - } finally { - wx.hideLoading() - } }, // 鎻愪氦璇勫 @@ -451,41 +479,53 @@ this.setData({ submitting: true }) wx.showLoading({ title: '鎻愪氦涓�...' }) - const { submissionId, scores, comment, totalScore } = this.data + 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 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, + ratings, + comment: commentText } 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 +533,7 @@ }, 1500) } } catch (error) { - console.error('鎻愪氦璇勫澶辫触:', error) + console.error('鎻愪氦璇勫垎澶辫触:', error) wx.showToast({ title: '鎻愪氦澶辫触', icon: 'error' @@ -507,41 +547,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 = submission?.participant?.phone + if (phone) { + wx.makePhoneCall({ + phoneNumber: phone + }) + } else { + wx.showToast({ + title: '鏆傛棤鑱旂郴鏂瑰紡', + icon: 'none' }) } - }, - - // 鑾峰彇璇勫垎绛夌骇鏂囨湰 - getScoreLabel(score) { - const option = this.data.scoreOptions.find(opt => opt.value === score) - return option ? option.label : `${score}鍒哷 }, // 鑾峰彇鏂囦欢澶у皬鏂囨湰 @@ -555,9 +578,47 @@ } }, + // 缁熶竴澶勭悊鎬у埆鏄剧ず鏂囨湰 + 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 formatDate(dateString, 'YYYY-MM-DD HH:mm') + return formatDateUtil(dateString, 'YYYY-MM-DD HH:mm') }, // 鍒嗕韩椤甸潰 @@ -567,4 +628,4 @@ path: '/pages/index/index' } } -}) \ No newline at end of file +}) -- Gitblit v1.8.0