Codex Assistant
14 小时以前 915d80766dd8e0157e9b9510b3634ed758eb5c5a
wx/pages/profile/profile.js
@@ -16,20 +16,31 @@
      awards: 0
    },
    
    // 我的报名记录
    registrations: [],
    registrationLoading: false,
    // 用户项目
    userProjects: [],
    projectsLoading: false,
    
    // 角色相关
    userRoles: [],
    isJudge: false,
    isOrganizer: false,
    hasPlayer: false,
    isEmployee: false,
    // 员工审核统计
    employeeReviewStats: {
      pendingCount: 0,
      approvedCount: 0,
      rejectedCount: 0
    },
    
    // 评委相关数据
    judgeStats: {
      pendingReviews: 0,
      completedReviews: 0,
      totalReviews: 0
      studentUnReviewedCount: 0
    },
    
    // 主办方相关数据
@@ -141,14 +152,14 @@
    
    this.loadUserInfo()
    this.loadUserStats()
    this.loadRecentRegistrations()
    this.loadUserProjects()
  },
  onShow() {
    // 页面显示时刷新数据
    this.loadUserInfo()
    this.loadUserStats()
    this.loadRecentRegistrations()
    this.loadUserProjects()
    // 初始化自定义 tabbar
    if (typeof this.getTabBar === 'function' && this.getTabBar()) {
      this.getTabBar().init()
@@ -161,13 +172,18 @@
  // 刷新数据
  async refreshData() {
    this.setData({ loading: true })
    try {
      await Promise.all([
        this.loadUserInfo(),
        this.loadUserStats(),
        this.loadRecentRegistrations()
        this.loadUserProjects()
      ])
    } catch (error) {
      console.error('刷新数据失败:', error)
    } finally {
      this.setData({ loading: false })
      wx.stopPullDownRefresh()
    }
  },
@@ -190,6 +206,18 @@
            grade
            roles
            createdAt
            employee {
              id
              name
              roleId
              description
            }
            player {
              id
              name
              phone
              description
            }
          }
        }
      `
@@ -203,6 +231,8 @@
        const userRoles = userInfo.roles || []
        const isJudge = userRoles.includes('JUDGE')
        const isOrganizer = userRoles.includes('ORGANIZER')
        const hasPlayer = userInfo.player && userInfo.player.id
        const isEmployee = !!(userInfo.employee && userInfo.employee.id)
        
        // 处理头像文字
        const avatarText = (userInfo.name || '用户').substring(0, 1)
@@ -212,7 +242,9 @@
          avatarText,
          userRoles,
          isJudge,
          isOrganizer
          isOrganizer,
          hasPlayer,
          isEmployee
        })
        
        // 更新全局用户信息
@@ -225,6 +257,18 @@
        
        if (isOrganizer) {
          this.loadOrganizerStats()
        }
        if (isEmployee) {
          this.loadEmployeeReviewStats()
        } else {
          this.setData({
            employeeReviewStats: {
              pendingCount: 0,
              approvedCount: 0,
              rejectedCount: 0
            }
          })
        }
      }
    } catch (error) {
@@ -265,74 +309,140 @@
  },
  // 加载最近报名记录
  async loadRecentRegistrations() {
  // 加载用户项目
  async loadUserProjects() {
    this.setData({ projectsLoading: true })
    try {
      this.setData({ registrationLoading: true })
      const query = `
        query GetRecentRegistrations {
          myRegistrations(limit: 5) {
        query GetMyProjects {
          myProjects {
            id
            activity {
              id
              title
              coverImage {
                id
                name
                path
                fullUrl
                fullThumbUrl
                mediaType
              }
              startTime
              status
            }
            projectName
            activityName
            status
            registrationTime
            statusText
            createTime
            submissionFiles {
              id
              name
              path
              fullUrl
              fullThumbUrl
              fileExt
              mediaType
            }
          }
        }
      `
      
      const result = await graphqlRequest(query)
      
      if (result && result.myRegistrations) {
        // 为每个活动添加标题文字
        const registrations = result.myRegistrations.map(registration => ({
          ...registration,
          activity: {
            ...registration.activity,
            titleText: (registration.activity.title || '活动').substring(0, 2)
      if (result && result.myProjects) {
        const projects = result.myProjects.map(project => {
          // 获取项目的第一个媒体文件作为缩略图
          let thumbnailUrl = ''
          let iconClass = this.getProjectIcon(project.status)
          if (project.submissionFiles && project.submissionFiles.length > 0) {
            const firstFile = project.submissionFiles[0]
            // 根据文件类型设置缩略图和图标
            if (firstFile.mediaType === 'IMAGE') {
              thumbnailUrl = firstFile.fullThumbUrl || firstFile.fullUrl
              iconClass = 'ic-image'
            } else if (firstFile.mediaType === 'VIDEO') {
              thumbnailUrl = firstFile.fullThumbUrl || firstFile.fullUrl
              iconClass = 'ic-video'
            } else if (firstFile.fileExt && firstFile.fileExt.toLowerCase() === 'pdf') {
              iconClass = 'ic-pdf'
            } else if (firstFile.fileExt && ['doc', 'docx'].includes(firstFile.fileExt.toLowerCase())) {
              iconClass = 'ic-word'
            } else {
              iconClass = 'ic-file'
            }
          }
        }))
        this.setData({
          registrations
          return {
            id: project.id,
            projectName: project.projectName,
            activityName: project.activityName,
            state: project.status,
            statusText: project.statusText || this.getProjectStatusText(project.status),
            statusType: this.getProjectStatusType(project.status),
            icon: iconClass,
            thumbnailUrl: thumbnailUrl,
            createTime: project.createTime
          }
        })
        this.setData({ userProjects: projects })
      }
    } catch (error) {
      console.error('加载报名记录失败:', error)
      console.error('加载用户项目失败:', error)
      // 不显示错误提示,静默失败
    } finally {
      this.setData({ registrationLoading: false })
      this.setData({ projectsLoading: false })
    }
  },
  // 加载员工审核统计
  async loadEmployeeReviewStats(keyword = null) {
    if (!this.data.isEmployee) {
      return
    }
    try {
      const query = `
        query EmployeeReviewStats($keyword: String) {
          employeeReviewStats(keyword: $keyword) {
            pendingCount
            approvedCount
            rejectedCount
          }
        }
      `
      const variables = {}
      if (keyword && typeof keyword === 'string' && keyword.trim()) {
        variables.keyword = keyword.trim()
      }
      const result = await graphqlRequest(query, variables)
      if (result && result.employeeReviewStats) {
        this.setData({
          employeeReviewStats: result.employeeReviewStats
        })
      }
    } catch (error) {
      console.error('加载员工审核统计失败:', error)
    }
  },
  // 加载评委统计数据
  async loadJudgeStats() {
    try {
      const query = `
        query GetJudgeStats {
          judgeStats {
            pendingReviews
            completedReviews
            totalReviews
        query GetReviewStatistics {
          reviewStatistics {
            unReviewedCount
            reviewedCount
            studentUnReviewedCount
          }
        }
      `
      
      const result = await graphqlRequest(query)
      
      if (result && result.judgeStats) {
        const judgeStats = result.judgeStats
      if (result && result.reviewStatistics) {
        const stats = result.reviewStatistics
        // 转换字段名以匹配现有的数据结构
        const judgeStats = {
          pendingReviews: stats.unReviewedCount,
          completedReviews: stats.reviewedCount,
          studentUnReviewedCount: stats.studentUnReviewedCount
        }
        
        this.setData({
          judgeStats,
@@ -376,14 +486,58 @@
    })
  },
  // 菜单项点击
  onMenuItemTap(e) {
    const { path } = e.currentTarget.dataset
  // 跳转到个人信息页面
  goToPersonalInfo() {
    wx.navigateTo({
      url: path
      url: '/pages/profile/personal-info'
    })
  },
  // 跳转到项目详情页面
  goToProjectDetail(e) {
    const projectId = e.currentTarget.dataset.projectId
    if (!projectId) {
      wx.showToast({
        title: '项目ID无效',
        icon: 'none'
      })
      return
    }
    // 如果是示例数据,显示提示
    if (projectId === 'registrations' || projectId === 'achievements') {
      wx.showToast({
        title: '这是示例项目,请选择真实项目',
        icon: 'none'
      })
      return
    }
    wx.navigateTo({
      url: `/pages/project/detail?id=${projectId}`
    })
  },
  // 跳转到评审页面
  goToReviewPage() {
    wx.navigateTo({
      url: '/pages/review/index'
    })
  },
  // 跳转到员工审核页面
  goToEmployeeReviewPage() {
    if (!this.data.isEmployee) {
      return
    }
    wx.navigateTo({
      url: '/pages/profile/employee-review'
    })
  },
  // 查看报名详情
  onRegistrationTap(e) {
@@ -448,53 +602,89 @@
    return formatDate(dateString, 'MM-DD HH:mm')
  },
  // 退出登录
  onLogout() {
    wx.showModal({
      title: '确认退出',
      content: '确定要退出登录吗?',
      success: (res) => {
        if (res.confirm) {
          this.logout()
        }
      }
    })
  // 获取项目状态文本(支持数字和字符串状态)
  getProjectStatusText(status) {
    // 数字状态映射(与web端保持一致)
    const numericStatusMap = {
      0: '未审核',
      1: '审核通过',
      2: '审核驳回',
      3: '已结束'
    }
    // 字符串状态映射
    const stringStatusMap = {
      'SUBMITTED': '已提交',
      'UNDER_REVIEW': '评审中',
      'REVIEWED': '已评审',
      'REJECTED': '已拒绝',
      'DRAFT': '草稿'
    }
    // 优先使用数字状态映射
    if (typeof status === 'number' && numericStatusMap[status]) {
      return numericStatusMap[status]
    }
    return stringStatusMap[status] || '未知状态'
  },
  // 执行退出登录
  async logout() {
    try {
      wx.showLoading({ title: '退出中...' })
      // 清除本地存储
      wx.removeStorageSync('token')
      wx.removeStorageSync('userInfo')
      // 清除全局数据
      app.globalData.token = ''
      app.globalData.userInfo = null
      app.globalData.isLoggedIn = false
      // 跳转到登录页
      wx.reLaunch({
        url: '/pages/login/login'
      })
      wx.showToast({
        title: '已退出登录',
        icon: 'success'
      })
    } catch (error) {
      console.error('退出登录失败:', error)
      wx.showToast({
        title: '退出失败',
        icon: 'error'
      })
    } finally {
      wx.hideLoading()
  // 获取项目状态类型(用于样式)
  getProjectStatusType(status) {
    // 数字状态类型映射(与web端保持一致)
    const numericTypeMap = {
      0: 'warning',   // 未审核
      1: 'success',   // 审核通过
      2: 'danger',    // 审核驳回
      3: 'info'       // 已结束
    }
    // 字符串状态类型映射
    const stringTypeMap = {
      'SUBMITTED': 'primary',
      'UNDER_REVIEW': 'warning',
      'REVIEWED': 'success',
      'REJECTED': 'danger',
      'DRAFT': 'info'
    }
    // 优先使用数字状态映射
    if (typeof status === 'number' && numericTypeMap[status]) {
      return numericTypeMap[status]
    }
    return stringTypeMap[status] || 'info'
  },
  // 获取项目图标
  getProjectIcon(status) {
    // 数字状态图标映射
    const numericIconMap = {
      0: '⏳',  // 未审核
      1: '✅',  // 审核通过
      2: '❌',  // 审核驳回
      3: '📋'   // 已结束
    }
    // 字符串状态图标映射
    const stringIconMap = {
      'SUBMITTED': '📋',
      'UNDER_REVIEW': '⏳',
      'REVIEWED': '✅',
      'REJECTED': '❌',
      'DRAFT': '📝'
    }
    // 优先使用数字状态映射
    if (typeof status === 'number' && numericIconMap[status]) {
      return numericIconMap[status]
    }
    return stringIconMap[status] || '📋'
  },
  // 分享页面
  onShareAppMessage() {
    return {