lrj
21 小时以前 dc643ba44fd2a426263015491268a0f0d6b4671d
wx/pages/message/message.js
@@ -1,587 +1,110 @@
// pages/message/message.js
const app = getApp()
const { graphqlRequest, formatDate, formatRelativeTime } = require('../../lib/utils')
Page({
  data: {
    loading: false,
    refreshing: false,
    loadingMore: false,
    hasMore: true,
    // 消息列表
    messages: [],
    // 分页参数
    pagination: {
      page: 1,
      limit: 20,
      total: 0
    },
    // 筛选条件
    filter: {
      type: 'ALL', // ALL, SYSTEM, ACTIVITY, JUDGE, ORGANIZER
      status: 'ALL' // ALL, READ, UNREAD
    },
    // 筛选选项
    typeOptions: [
      { value: 'ALL', label: '全部消息' },
      { value: 'SYSTEM', label: '系统通知' },
      { value: 'ACTIVITY', label: '活动消息' },
      { value: 'JUDGE', label: '评审消息' },
      { value: 'ORGANIZER', label: '主办方消息' }
    ],
    statusOptions: [
      { value: 'ALL', label: '全部状态' },
      { value: 'UNREAD', label: '未读' },
      { value: 'READ', label: '已读' }
    ],
    // 显示筛选面板
    showFilter: false,
    // 统计数据
    stats: {
      total: 0,
      unread: 0
    },
    // 选择模式
    selectMode: false,
    selectedMessages: [],
    // 消息类型图标映射
    typeIcons: {
      'SYSTEM': '🔔',
      'ACTIVITY': '🎯',
      'JUDGE': '⚖️',
      'ORGANIZER': '👥',
      'REGISTRATION': '📝',
      'RESULT': '🏆',
      'REMINDER': '⏰'
    }
    loading: false
  },
  onLoad() {
    this.loadMessages()
    this.loadMessageStats()
  },
  onShow() {
    // 页面显示时刷新未读数量
    this.loadMessageStats()
    // 初始化自定义 tabbar
    if (typeof this.getTabBar === 'function' && this.getTabBar()) {
      this.getTabBar().init()
    }
    this.loadMessages()
  },
  onPullDownRefresh() {
    this.refreshMessages()
  },
  onReachBottom() {
    if (this.data.hasMore && !this.data.loadingMore) {
      this.loadMoreMessages()
    }
    this.loadMessages()
  },
  // 加载消息列表
  async loadMessages(reset = true) {
    try {
      if (reset) {
        this.setData({
          loading: true,
          pagination: { ...this.data.pagination, page: 1 }
        })
      }
      const { filter, pagination } = this.data
      const query = `
        query GetMessages($input: MessageQueryInput!) {
          messages(input: $input) {
            items {
              id
              type
              title
              content
              isRead
              createdAt
              data
              relatedActivity {
                id
                title
              }
            }
            pagination {
              total
              page
              limit
              hasMore
            }
          }
        }
      `
      const input = {
        type: filter.type === 'ALL' ? null : filter.type,
        status: filter.status === 'ALL' ? null : filter.status,
        page: pagination.page,
        limit: pagination.limit
      }
      const result = await graphqlRequest(query, { input })
      if (result && result.messages) {
        const { items, pagination: newPagination } = result.messages
        // 为每个消息添加选中状态
        const processedItems = items.map(item => ({
          ...item,
          isSelected: this.data.selectedMessages.indexOf(item.id) > -1
        }))
        this.setData({
          messages: reset ? processedItems : [...this.data.messages, ...processedItems],
          pagination: newPagination,
          hasMore: newPagination.hasMore
        })
      }
    } catch (error) {
      console.error('加载消息失败:', error)
  loadMessages() {
    // 检查用户是否已登录
    const userInfo = app.globalData.userInfo
    if (!userInfo || !userInfo.userId) {
      console.error('用户未登录或userId不存在')
      wx.showToast({
        title: '加载失败',
        icon: 'error'
      })
    } finally {
      this.setData({
        loading: false,
        refreshing: false,
        loadingMore: false
      })
      wx.stopPullDownRefresh()
    }
  },
  // 刷新消息
  async refreshMessages() {
    this.setData({ refreshing: true })
    await this.loadMessages(true)
    await this.loadMessageStats()
  },
  // 加载更多消息
  async loadMoreMessages() {
    if (!this.data.hasMore || this.data.loadingMore) return
    this.setData({
      loadingMore: true,
      pagination: {
        ...this.data.pagination,
        page: this.data.pagination.page + 1
      }
    })
    await this.loadMessages(false)
  },
  // 加载消息统计
  async loadMessageStats() {
    try {
      const query = `
        query GetMessageStats {
          messageStats {
            total
            unread
          }
        }
      `
      const result = await graphqlRequest(query)
      if (result && result.messageStats) {
        this.setData({
          stats: result.messageStats
        })
        // 更新底部导航栏的未读数量
        if (typeof this.getTabBar === 'function' && this.getTabBar()) {
          this.getTabBar().setData({
            unreadCount: result.messageStats.unread
          })
        }
      }
    } catch (error) {
      console.error('加载消息统计失败:', error)
    }
  },
  // 消息点击
  async onMessageTap(e) {
    const { id, index } = e.currentTarget.dataset
    const message = this.data.messages[index]
    // 如果是选择模式,切换选择状态
    if (this.data.selectMode) {
      this.toggleMessageSelection(id)
      return
    }
    // 标记为已读
    if (!message.isRead) {
      await this.markAsRead(id, index)
    }
    // 根据消息类型处理跳转
    this.handleMessageAction(message)
  },
  // 处理消息动作
  handleMessageAction(message) {
    const { type, data, relatedActivity } = message
    switch (type) {
      case 'ACTIVITY':
        if (relatedActivity && relatedActivity.id) {
          wx.navigateTo({
            url: `/pages/activity/detail?id=${relatedActivity.id}`
          })
        }
        break
      case 'REGISTRATION':
        if (data && data.registrationId) {
          wx.navigateTo({
            url: `/pages/profile/registration-detail?id=${data.registrationId}`
          })
        }
        break
      case 'JUDGE':
        if (data && data.submissionId) {
          wx.navigateTo({
            url: `/pages/judge/review?id=${data.submissionId}`
          })
        }
        break
      case 'ORGANIZER':
        if (relatedActivity && relatedActivity.id) {
          wx.navigateTo({
            url: `/pages/organizer/activity-detail?id=${relatedActivity.id}`
          })
        }
        break
      default:
        // 显示消息详情
        this.showMessageDetail(message)
        break
    }
  },
  // 显示消息详情
  showMessageDetail(message) {
    wx.showModal({
      title: message.title,
      content: message.content,
      showCancel: false,
      confirmText: '知道了'
    })
  },
  // 标记为已读
  async markAsRead(messageId, index) {
    try {
      const mutation = `
        mutation MarkMessageAsRead($id: ID!) {
          markMessageAsRead(id: $id) {
            success
          }
        }
      `
      const result = await graphqlRequest(mutation, { id: messageId })
      if (result && result.markMessageAsRead.success) {
        // 更新本地数据
        const messages = [...this.data.messages]
        messages[index].isRead = true
        this.setData({ messages })
        // 更新统计数据
        this.setData({
          'stats.unread': Math.max(0, this.data.stats.unread - 1)
        })
      }
    } catch (error) {
      console.error('标记已读失败:', error)
    }
  },
  // 批量标记为已读
  async markAllAsRead() {
    try {
      wx.showLoading({ title: '处理中...' })
      const mutation = `
        mutation MarkAllMessagesAsRead {
          markAllMessagesAsRead {
            success
            count
          }
        }
      `
      const result = await graphqlRequest(mutation)
      if (result && result.markAllMessagesAsRead.success) {
        // 刷新消息列表
        await this.refreshMessages()
        wx.showToast({
          title: '已全部标记为已读',
          icon: 'success'
        })
      }
    } catch (error) {
      console.error('批量标记已读失败:', error)
      wx.showToast({
        title: '操作失败',
        icon: 'error'
      })
    } finally {
      wx.hideLoading()
    }
  },
  // 删除消息
  async deleteMessage(messageId) {
    try {
      const mutation = `
        mutation DeleteMessage($id: ID!) {
          deleteMessage(id: $id) {
            success
          }
        }
      `
      const result = await graphqlRequest(mutation, { id: messageId })
      if (result && result.deleteMessage.success) {
        // 从列表中移除
        const messages = this.data.messages.filter(msg => msg.id !== messageId)
        this.setData({ messages })
        wx.showToast({
          title: '删除成功',
          icon: 'success'
        })
      }
    } catch (error) {
      console.error('删除消息失败:', error)
      wx.showToast({
        title: '删除失败',
        icon: 'error'
      })
    }
  },
  // 长按消息
  onMessageLongPress(e) {
    const { id } = e.currentTarget.dataset
    wx.showActionSheet({
      itemList: ['标记为已读', '删除消息'],
      success: (res) => {
        switch (res.tapIndex) {
          case 0:
            this.markAsRead(id)
            break
          case 1:
            wx.showModal({
              title: '确认删除',
              content: '确定要删除这条消息吗?',
              success: (modalRes) => {
                if (modalRes.confirm) {
                  this.deleteMessage(id)
                }
              }
            })
            break
        }
      }
    })
  },
  // 切换筛选面板
  onToggleFilter() {
    this.setData({
      showFilter: !this.data.showFilter
    })
  },
  // 筛选条件改变
  onFilterChange(e) {
    const { type, value } = e.currentTarget.dataset
    this.setData({
      [`filter.${type}`]: value,
      showFilter: false
    })
    // 重新加载消息
    this.loadMessages(true)
  },
  // 切换选择模式
  onToggleSelectMode() {
    this.setData({
      selectMode: !this.data.selectMode,
      selectedMessages: []
    })
  },
  // 切换消息选择状态
  toggleMessageSelection(messageId) {
    const selectedMessages = [...this.data.selectedMessages]
    const index = selectedMessages.indexOf(messageId)
    if (index > -1) {
      selectedMessages.splice(index, 1)
    } else {
      selectedMessages.push(messageId)
    }
    // 同时更新消息的isSelected字段
    const messages = this.data.messages.map(msg => ({
      ...msg,
      isSelected: selectedMessages.indexOf(msg.id) > -1
    }))
    this.setData({
      selectedMessages,
      messages
    })
  },
  // 全选/取消全选
  onToggleSelectAll() {
    const { messages, selectedMessages } = this.data
    const allSelected = selectedMessages.length === messages.length
    const newSelectedMessages = allSelected ? [] : messages.map(msg => msg.id)
    // 同时更新消息的isSelected字段
    const updatedMessages = messages.map(msg => ({
      ...msg,
      isSelected: !allSelected
    }))
    this.setData({
      selectedMessages: newSelectedMessages,
      messages: updatedMessages
    })
  },
  // 批量删除选中消息
  async onDeleteSelected() {
    const { selectedMessages } = this.data
    if (selectedMessages.length === 0) {
      wx.showToast({
        title: '请选择要删除的消息',
        title: '请先登录',
        icon: 'error'
      })
      return
    }
    this.setData({ loading: true })
    
    wx.showModal({
      title: '确认删除',
      content: `确定要删除选中的 ${selectedMessages.length} 条消息吗?`,
      success: async (res) => {
        if (res.confirm) {
          try {
            wx.showLoading({ title: '删除中...' })
            const mutation = `
              mutation DeleteMessages($ids: [ID!]!) {
                deleteMessages(ids: $ids) {
                  success
                  count
                }
              }
            `
            const result = await graphqlRequest(mutation, { ids: selectedMessages })
            if (result && result.deleteMessages.success) {
              // 刷新消息列表
              await this.refreshMessages()
              this.setData({
                selectMode: false,
                selectedMessages: []
              })
              wx.showToast({
                title: `已删除 ${result.deleteMessages.count} 条消息`,
                icon: 'success'
              })
            }
          } catch (error) {
            console.error('批量删除失败:', error)
            wx.showToast({
              title: '删除失败',
              icon: 'error'
            })
          } finally {
            wx.hideLoading()
          }
    const query = `
      query GetMessagesByUserId($userId: Long!) {
        getMessagesByUserId(userId: $userId) {
          id
          userId
          content
          wxMsgSuccess
          wxMsgErrCount
          state
          createTime
          updateTime
        }
      }
    })
  },
    `
  // 获取消息类型图标
  getTypeIcon(type) {
    return this.data.typeIcons[type] || '📨'
  },
  // 获取消息类型文本
  getTypeText(type) {
    const typeMap = {
      'SYSTEM': '系统通知',
      'ACTIVITY': '活动消息',
      'JUDGE': '评审消息',
      'ORGANIZER': '主办方消息',
      'REGISTRATION': '报名消息',
      'RESULT': '结果通知',
      'REMINDER': '提醒消息'
    const variables = {
      userId: userInfo.userId
    }
    return typeMap[type] || '消息'
    app.graphqlRequest(query, variables)
      .then(data => {
        console.log('消息数据:', data)
        this.setData({
          messages: data.getMessagesByUserId || [],
          loading: false
        })
        wx.stopPullDownRefresh()
      })
      .catch(error => {
        console.error('加载消息失败:', error)
        wx.showToast({
          title: '加载失败',
          icon: 'error'
        })
        this.setData({ loading: false })
        wx.stopPullDownRefresh()
      })
  },
  // 格式化时间
  formatTime(dateString) {
    const now = new Date()
    const date = new Date(dateString)
    const diff = now.getTime() - date.getTime()
  // 格式化消息时间
  formatMessageTime(timeStr) {
    if (!timeStr) return ''
    
    // 一天内显示相对时间
    if (diff < 24 * 60 * 60 * 1000) {
      return formatRelativeTime(dateString)
    }
    // 超过一天显示具体日期
    return formatDate(dateString, 'MM-DD HH:mm')
  },
  // 分享页面
  onShareAppMessage() {
    return {
      title: '蓉易创 - 消息中心',
      path: '/pages/index/index'
    try {
      const date = new Date(timeStr)
      const now = new Date()
      const diff = now.getTime() - date.getTime()
      // 如果是今天
      if (diff < 24 * 60 * 60 * 1000) {
        const hours = date.getHours().toString().padStart(2, '0')
        const minutes = date.getMinutes().toString().padStart(2, '0')
        return `${hours}:${minutes}`
      }
      // 如果是昨天
      if (diff < 48 * 60 * 60 * 1000) {
        const hours = date.getHours().toString().padStart(2, '0')
        const minutes = date.getMinutes().toString().padStart(2, '0')
        return `昨天 ${hours}:${minutes}`
      }
      // 其他日期
      const month = (date.getMonth() + 1).toString().padStart(2, '0')
      const day = date.getDate().toString().padStart(2, '0')
      const hours = date.getHours().toString().padStart(2, '0')
      const minutes = date.getMinutes().toString().padStart(2, '0')
      return `${month}-${day} ${hours}:${minutes}`
    } catch (error) {
      console.error('时间格式化失败:', error)
      return timeStr
    }
  }
})