// pages/project/detail.js
|
const app = getApp()
|
|
Page({
|
data: {
|
projectId: '',
|
projectDetail: null,
|
timeline: [],
|
loading: true,
|
error: '',
|
statusText: '',
|
genderText: '',
|
educationText: '',
|
timelineLoading: false,
|
timelineError: '',
|
showRatingDetail: false,
|
ratingDetail: null,
|
ratingDetailLoading: false,
|
ratingDetailError: ''
|
},
|
|
onLoad(options) {
|
if (options.id) {
|
this.setData({
|
projectId: options.id
|
})
|
this.loadProjectDetail()
|
} else {
|
this.setData({
|
error: '缺少项目ID参数',
|
loading: false
|
})
|
}
|
},
|
|
// 加载项目详情
|
async loadProjectDetail() {
|
try {
|
this.setData({
|
loading: true,
|
error: ''
|
})
|
|
const projectDetail = await this.getProjectDetailFromAPI(this.data.projectId)
|
|
if (!projectDetail) {
|
throw new Error('项目详情获取失败')
|
}
|
|
if (projectDetail.submissionFiles) {
|
projectDetail.submissionFiles.forEach(file => {
|
file.fileSizeText = this.formatFileSize(file.fileSize)
|
})
|
}
|
|
this.setData({
|
projectDetail,
|
statusText: this.getStatusText(projectDetail.state),
|
genderText: this.getGenderText(projectDetail.playerInfo?.gender),
|
educationText: this.getEducationText(projectDetail.playerInfo?.education)
|
})
|
|
await this.loadProjectTimeline(this.data.projectId)
|
} catch (error) {
|
console.error('加载项目详情失败:', error)
|
this.setData({
|
error: error.message || '加载失败,请重试'
|
})
|
} finally {
|
this.setData({ loading: false })
|
}
|
},
|
|
// 从API获取项目详情
|
async getProjectDetailFromAPI(projectId) {
|
// 构建GraphQL查询
|
const query = `
|
query GetProjectDetail($id: ID!) {
|
activityPlayerDetail(id: $id) {
|
id
|
playerInfo {
|
id
|
name
|
phone
|
gender
|
birthday
|
education
|
introduction
|
description
|
avatarUrl
|
avatar {
|
id
|
fullUrl
|
fullThumbUrl
|
name
|
fileSize
|
fileExt
|
}
|
userInfo {
|
userId
|
name
|
phone
|
avatarUrl
|
}
|
}
|
regionInfo {
|
id
|
name
|
fullPath
|
}
|
activityName
|
projectName
|
description
|
feedback
|
state
|
stageId
|
submissionFiles {
|
id
|
fullUrl
|
fullThumbUrl
|
name
|
fileSize
|
fileExt
|
mediaType
|
}
|
ratingForm {
|
schemeId
|
schemeName
|
items {
|
id
|
name
|
maxScore
|
orderNo
|
}
|
totalMaxScore
|
}
|
}
|
}
|
`
|
|
try {
|
const result = await app.graphqlRequest(query, { id: projectId })
|
return result.activityPlayerDetail
|
} catch (error) {
|
throw error
|
}
|
},
|
|
async loadProjectTimeline(activityPlayerId) {
|
if (!activityPlayerId) {
|
return
|
}
|
|
const idNumber = Number(activityPlayerId)
|
const variables = {
|
activityPlayerId: Number.isNaN(idNumber) ? activityPlayerId : idNumber
|
}
|
|
this.setData({
|
timelineLoading: true,
|
timelineError: ''
|
})
|
|
const query = `
|
query ProjectStageTimeline($activityPlayerId: ID!) {
|
projectStageTimeline(activityPlayerId: $activityPlayerId) {
|
activityId
|
activityName
|
stages {
|
stageId
|
stageName
|
matchTime
|
sortOrder
|
participated
|
activityPlayerId
|
averageScore
|
ratingCount
|
hasRating
|
latestRatingTime
|
}
|
}
|
}
|
`
|
|
try {
|
const result = await app.graphqlRequest(query, variables)
|
const projectStageTimeline = result && result.projectStageTimeline ? result.projectStageTimeline : null
|
const stages = projectStageTimeline && projectStageTimeline.stages ? projectStageTimeline.stages : []
|
|
const timeline = stages.map(stage => {
|
const hasScore = stage.hasRating && stage.averageScore !== null && stage.averageScore !== undefined
|
let scoreText = '未参赛'
|
if (stage.participated) {
|
scoreText = hasScore ? `平均分:${Number(stage.averageScore).toFixed(2)}` : '未评分'
|
}
|
|
return {
|
...stage,
|
matchTimeText: stage.matchTime ? this.formatDateTime(stage.matchTime) : '',
|
scoreText,
|
displayAverageScore: hasScore ? Number(stage.averageScore).toFixed(2) : null,
|
isClickable: stage.participated && hasScore && !!stage.activityPlayerId
|
}
|
})
|
|
this.setData({
|
timeline,
|
timelineLoading: false
|
})
|
} catch (error) {
|
console.error('加载阶段时间轴失败:', error)
|
this.setData({
|
timelineError: error.message || '时间轴加载失败',
|
timelineLoading: false
|
})
|
}
|
},
|
|
async fetchStageRatingDetail(activityPlayerId) {
|
const idNumber = Number(activityPlayerId)
|
const variables = {
|
activityPlayerId: Number.isNaN(idNumber) ? activityPlayerId : idNumber
|
}
|
|
const query = `
|
query StageJudgeRatings($activityPlayerId: ID!) {
|
stageJudgeRatings(activityPlayerId: $activityPlayerId) {
|
activityPlayerId
|
stageId
|
stageName
|
matchTime
|
ratingCount
|
averageScore
|
judgeRatings {
|
judgeId
|
judgeName
|
totalScore
|
feedback
|
ratingTime
|
}
|
}
|
}
|
`
|
|
const result = await app.graphqlRequest(query, variables)
|
const detail = result && result.stageJudgeRatings ? result.stageJudgeRatings : null
|
|
const sourceJudgeRatings = detail && detail.judgeRatings ? detail.judgeRatings : []
|
const judgeRatings = sourceJudgeRatings.map(item => ({
|
...item,
|
totalScoreText: item.totalScore !== null && item.totalScore !== undefined ? `${Number(item.totalScore).toFixed(2)}分` : '未评分',
|
ratingTimeText: item.ratingTime ? this.formatDateTime(item.ratingTime) : ''
|
}))
|
|
const averageScoreValue = detail && detail.averageScore !== undefined && detail.averageScore !== null
|
? detail.averageScore
|
: null
|
|
return {
|
activityPlayerId: detail && detail.activityPlayerId ? detail.activityPlayerId : variables.activityPlayerId,
|
stageId: detail && detail.stageId ? detail.stageId : null,
|
stageName: detail && detail.stageName ? detail.stageName : '阶段信息',
|
matchTime: detail && detail.matchTime ? detail.matchTime : null,
|
matchTimeText: detail && detail.matchTime ? this.formatDateTime(detail.matchTime) : '',
|
ratingCount: detail && detail.ratingCount ? detail.ratingCount : 0,
|
averageScore: averageScoreValue,
|
averageScoreText: averageScoreValue !== null ? Number(averageScoreValue).toFixed(2) : '暂无评分',
|
judgeRatings
|
}
|
},
|
|
async openStageDetail(e) {
|
const { playerId } = e.currentTarget.dataset
|
const clickable = e.currentTarget.dataset.clickable === true || e.currentTarget.dataset.clickable === 'true'
|
const participated = e.currentTarget.dataset.participated === true || e.currentTarget.dataset.participated === 'true'
|
|
if (!playerId || !participated) {
|
return
|
}
|
|
if (!clickable) {
|
wx.showToast({
|
title: '暂无评分',
|
icon: 'none'
|
})
|
return
|
}
|
|
this.setData({
|
showRatingDetail: true,
|
ratingDetailLoading: true,
|
ratingDetailError: '',
|
ratingDetail: { judgeRatings: [] }
|
})
|
|
try {
|
const detail = await this.fetchStageRatingDetail(playerId)
|
this.setData({
|
ratingDetail: detail,
|
ratingDetailLoading: false
|
})
|
} catch (error) {
|
console.error('加载阶段评分详情失败:', error)
|
this.setData({
|
ratingDetailError: error.message || '加载失败',
|
ratingDetailLoading: false
|
})
|
}
|
},
|
|
closeStageDetail() {
|
this.setData({
|
showRatingDetail: false,
|
ratingDetail: null,
|
ratingDetailError: ''
|
})
|
},
|
|
// 预览文件
|
previewFile(e) {
|
const file = e.currentTarget.dataset.file
|
|
if (!file || !file.fullUrl) {
|
wx.showToast({
|
title: '文件链接无效',
|
icon: 'none'
|
})
|
return
|
}
|
|
// 根据文件类型进行不同处理
|
const fileExt = (file.fileExt || '').toLowerCase()
|
|
if (this.isImageFile(fileExt)) {
|
// 图片预览
|
wx.previewImage({
|
urls: [file.fullUrl],
|
current: file.fullUrl
|
})
|
} else if (this.isVideoFile(fileExt)) {
|
// 视频预览
|
wx.navigateTo({
|
url: `/pages/video/video?url=${encodeURIComponent(file.fullUrl)}&title=${encodeURIComponent(file.name)}`
|
})
|
} else if (this.isPdfFile(fileExt)) {
|
// PDF文档预览
|
this.previewPdfFile(file)
|
} else if (this.isOfficeFile(fileExt)) {
|
// Office文档预览
|
this.previewOfficeFile(file)
|
} else {
|
// 其他文件类型,尝试下载打开
|
this.downloadAndOpenFile(file)
|
}
|
},
|
|
// PDF文档预览
|
previewPdfFile(file) {
|
wx.showLoading({
|
title: '正在打开PDF...'
|
})
|
|
wx.downloadFile({
|
url: file.fullUrl,
|
success: (downloadRes) => {
|
wx.hideLoading()
|
if (downloadRes.statusCode === 200) {
|
wx.openDocument({
|
filePath: downloadRes.tempFilePath,
|
fileType: 'pdf',
|
success: () => {
|
console.log('PDF打开成功')
|
},
|
fail: (error) => {
|
console.error('PDF打开失败:', error)
|
wx.showToast({
|
title: 'PDF打开失败',
|
icon: 'none'
|
})
|
}
|
})
|
} else {
|
wx.showToast({
|
title: 'PDF下载失败',
|
icon: 'none'
|
})
|
}
|
},
|
fail: (error) => {
|
wx.hideLoading()
|
console.error('PDF下载失败:', error)
|
wx.showToast({
|
title: 'PDF下载失败',
|
icon: 'none'
|
})
|
}
|
})
|
},
|
|
// Office文档预览
|
previewOfficeFile(file) {
|
const fileExt = (file.fileExt || '').toLowerCase()
|
|
wx.showLoading({
|
title: '正在打开文档...',
|
mask: true
|
})
|
|
wx.downloadFile({
|
url: file.fullUrl,
|
success: (downloadRes) => {
|
wx.hideLoading()
|
if (downloadRes.statusCode === 200) {
|
// 文件类型映射
|
const fileTypeMap = {
|
'doc': 'doc',
|
'docx': 'docx',
|
'xls': 'xls',
|
'xlsx': 'xlsx',
|
'ppt': 'ppt',
|
'pptx': 'pptx'
|
}
|
|
wx.openDocument({
|
filePath: downloadRes.tempFilePath,
|
fileType: fileTypeMap[fileExt] || 'doc',
|
success: () => {
|
console.log('文档打开成功')
|
},
|
fail: (error) => {
|
console.error('文档打开失败:', error)
|
wx.showModal({
|
title: '打开失败',
|
content: '文档打开失败,可能是文件格式不支持或文件损坏',
|
showCancel: false,
|
confirmText: '确定'
|
})
|
}
|
})
|
} else {
|
wx.showToast({
|
title: '文件下载失败',
|
icon: 'none'
|
})
|
}
|
},
|
fail: (error) => {
|
wx.hideLoading()
|
console.error('文档下载失败:', error)
|
wx.showToast({
|
title: '文件下载失败',
|
icon: 'none'
|
})
|
}
|
})
|
},
|
|
// 下载并打开文件
|
downloadAndOpenFile(file) {
|
wx.showModal({
|
title: '文件预览',
|
content: '是否要下载并打开此文件?',
|
confirmText: '下载',
|
cancelText: '取消',
|
success: (res) => {
|
if (res.confirm) {
|
wx.showLoading({
|
title: '正在下载...'
|
})
|
|
wx.downloadFile({
|
url: file.fullUrl,
|
success: (downloadRes) => {
|
wx.hideLoading()
|
if (downloadRes.statusCode === 200) {
|
wx.openDocument({
|
filePath: downloadRes.tempFilePath,
|
success: () => {
|
console.log('文档打开成功')
|
},
|
fail: (error) => {
|
console.error('文档打开失败:', error)
|
wx.showToast({
|
title: '文件打开失败',
|
icon: 'none'
|
})
|
}
|
})
|
} else {
|
wx.showToast({
|
title: '文件下载失败',
|
icon: 'none'
|
})
|
}
|
},
|
fail: (error) => {
|
wx.hideLoading()
|
console.error('文件下载失败:', error)
|
wx.showToast({
|
title: '文件下载失败',
|
icon: 'none'
|
})
|
}
|
})
|
}
|
}
|
})
|
},
|
|
// 判断是否为图片文件
|
isImageFile(fileExt) {
|
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
|
return imageExts.includes(fileExt)
|
},
|
|
// 判断是否为视频文件
|
isVideoFile(fileExt) {
|
const videoExts = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm']
|
return videoExts.includes(fileExt)
|
},
|
|
// 判断是否为PDF文件
|
isPdfFile(fileExt) {
|
return fileExt === 'pdf'
|
},
|
|
// 判断是否为Office文件
|
isOfficeFile(fileExt) {
|
const officeExts = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'dotx', 'xlsb', 'xlsm', 'ppsx', 'pps', 'potx', 'ppsm']
|
return officeExts.includes(fileExt)
|
},
|
|
// 格式化文件大小
|
formatFileSize(bytes) {
|
if (!bytes || bytes === 0) return '0 B'
|
|
const k = 1024
|
const sizes = ['B', 'KB', 'MB', 'GB']
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
},
|
|
// 格式化日期时间
|
formatDateTime(dateTimeStr) {
|
if (!dateTimeStr) return ''
|
|
try {
|
const date = new Date(dateTimeStr)
|
const year = date.getFullYear()
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
const day = String(date.getDate()).padStart(2, '0')
|
const hours = String(date.getHours()).padStart(2, '0')
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
|
return `${year}-${month}-${day} ${hours}:${minutes}`
|
} catch (error) {
|
return dateTimeStr
|
}
|
},
|
|
// 获取状态文本
|
getStatusText(state) {
|
const statusMap = {
|
0: '未审核',
|
1: '审核通过',
|
2: '审核不通过'
|
}
|
return statusMap[state] || '未知状态'
|
},
|
|
// 获取性别文本
|
getGenderText(gender) {
|
const genderMap = {
|
'MALE': '男',
|
'FEMALE': '女'
|
}
|
return genderMap[gender] || ''
|
},
|
|
// 获取学历文本
|
getEducationText(education) {
|
const educationMap = {
|
'HIGH_SCHOOL': '高中及以下',
|
'COLLEGE': '大专',
|
'BACHELOR': '本科',
|
'MASTER': '硕士',
|
'DOCTOR': '博士'
|
}
|
return educationMap[education] || ''
|
},
|
|
// 分享功能
|
onShareAppMessage() {
|
return {
|
title: `项目详情 - ${this.data.projectDetail?.projectName || ''}`,
|
path: `/pages/project/detail?id=${this.data.projectId}`
|
}
|
}
|
})
|