index.vue 6.87 KB
<template>
  <view class="message-page">
    <view class="page-content">
      <view class="hero-card">
        <view class="hero-head">
          <text class="hero-title">资讯</text>
          <text class="env-tag" :class="{ mock: current_env_use_mock }">
            {{ current_env_label }}
          </text>
        </view>
        <text class="hero-desc">
          当前环境由配置文件统一控制。本地如果要让所有接口都走 mock,只需要改 `config/dev.js` 里的 `API_RUNTIME_ENV`。
        </text>
      </view>

      <view class="toolbar-card">
        <view>
          <text class="section-title">列表状态</text>
          <text class="section-desc">
            共 {{ total }} 条资讯,未读 {{ unread_count }} 条。进入详情页后,当前资讯会在 mock 中自动变成已读。
          </text>
        </view>
        <button class="refresh-btn" :loading="loading" @tap="handleRefresh">
          刷新
        </button>
      </view>

      <view v-if="loading && !message_list.length" class="placeholder-card">
        <text class="section-title">加载中</text>
        <text class="section-desc">
          正在拉取资讯列表...
        </text>
      </view>

      <view v-else-if="!message_list.length" class="placeholder-card">
        <text class="section-title">暂无资讯</text>
        <text class="section-desc">
          当前接口还没有返回资讯内容,可以先在 mock 里补结构,再回到这个页面验证展示效果。
        </text>
      </view>

      <view
        v-for="item in message_list"
        :key="item.id"
        class="message-card"
        @tap="goToDetail(item.id)"
      >
        <view class="message-top">
          <view class="message-meta">
            <text class="message-category">{{ item.category }}</text>
            <text class="message-time">{{ item.created_time }}</text>
          </view>
          <text class="message-status" :class="{ unread: item.status === 'send' }">
            {{ item.status === 'send' ? '未读' : '已读' }}
          </text>
        </view>
        <text class="message-title">{{ item.title }}</text>
        <text class="message-summary">{{ item.summary }}</text>
      </view>

      <button
        v-if="has_more && message_list.length"
        class="load-more-btn"
        :loading="loading_more"
        @tap="handleLoadMore"
      >
        加载更多
      </button>
    </view>

    <AppTabbar current="message" />
  </view>
</template>

<script setup>
import { computed, ref } from 'vue'
import Taro, { useDidShow, useLoad } from '@tarojs/taro'
import AppTabbar from '@/components/AppTabbar.vue'
import { messageListAPI } from '@/api/message'
import { getCurrentApiConfig } from '@/utils/config'

const api_config = getCurrentApiConfig()
const current_env_label = api_config.label
const current_env_use_mock = api_config.useMock
const message_list = ref([])
const page = ref(0)
const page_size = 6
const total = ref(0)
const has_more = ref(true)
const loading = ref(false)
const loading_more = ref(false)
const has_loaded_once = ref(false)

const unread_count = computed(() => (
  message_list.value.filter((item) => item.status === 'send').length
))

const fetchMessageList = async (nextPage = 0, append = false) => {
  if (append) {
    loading_more.value = true
  } else {
    loading.value = true
  }

  try {
    const response = await messageListAPI({
      page: nextPage,
      limit: page_size,
    })

    if (response?.code !== 1) {
      Taro.showToast({
        title: response?.msg || '获取资讯失败',
        icon: 'none',
      })
      return
    }

    const list = response?.data?.list || []
    message_list.value = append ? [...message_list.value, ...list] : list
    total.value = Number(response?.data?.total || list.length)
    has_more.value = !!response?.data?.has_more
    page.value = nextPage
  } catch (error) {
    console.error('获取资讯列表失败:', error)
  } finally {
    loading.value = false
    loading_more.value = false
  }
}

const handleRefresh = async () => {
  await fetchMessageList(0, false)
}

const handleLoadMore = async () => {
  if (!has_more.value || loading_more.value) return
  await fetchMessageList(page.value + 1, true)
}

const goToDetail = (id) => {
  Taro.navigateTo({
    url: `/pages/message-detail/index?id=${encodeURIComponent(id)}`,
  })
}

useLoad(async () => {
  await fetchMessageList(0, false)
  has_loaded_once.value = true
})

useDidShow(async () => {
  if (!has_loaded_once.value) return
  await fetchMessageList(0, false)
})
</script>

<style lang="less">
.message-page {
  min-height: 100vh;
  background:
    radial-gradient(circle at top left, rgba(166, 121, 57, 0.12), transparent 28%),
    linear-gradient(180deg, #fffaf4 0%, #f4f6fb 100%);

  .page-content {
    padding: 32rpx 24rpx 0;
    box-sizing: border-box;
  }

  .hero-card,
  .toolbar-card,
  .message-card,
  .placeholder-card {
    padding: 32rpx;
    border-radius: 28rpx;
    background: rgba(255, 255, 255, 0.94);
    border: 2rpx solid rgba(166, 121, 57, 0.08);
    box-shadow: 0 20rpx 60rpx rgba(15, 23, 42, 0.06);
    box-sizing: border-box;
  }

  .hero-head,
  .toolbar-card,
  .message-top,
  .message-meta {
    display: flex;
    align-items: center;
  }

  .hero-head,
  .toolbar-card,
  .message-top {
    justify-content: space-between;
  }

  .hero-title,
  .section-title {
    display: block;
    font-size: 40rpx;
    font-weight: 700;
    color: #111827;
  }

  .hero-desc,
  .section-desc {
    display: block;
    margin-top: 16rpx;
    font-size: 26rpx;
    line-height: 1.8;
    color: #6b7280;
  }

  .placeholder-card {
    margin-top: 24rpx;
  }

  .toolbar-card,
  .message-card {
    margin-top: 24rpx;
  }

  .section-title {
    font-size: 30rpx;
  }

  .env-tag {
    padding: 10rpx 18rpx;
    border-radius: 999rpx;
    font-size: 22rpx;
    color: #1d4ed8;
    background: #dbeafe;
  }

  .env-tag.mock {
    color: #166534;
    background: #dcfce7;
  }

  .refresh-btn,
  .load-more-btn {
    border-radius: 999rpx;
    font-size: 26rpx;
    line-height: 80rpx;
  }

  .refresh-btn {
    min-width: 180rpx;
    color: #0f172a;
    background: #fff;
    border: 2rpx solid #d1d5db;
  }

  .message-card {
    display: flex;
    flex-direction: column;
    gap: 18rpx;
  }

  .message-meta {
    gap: 14rpx;
  }

  .message-category,
  .message-time,
  .message-status {
    font-size: 22rpx;
  }

  .message-category {
    padding: 8rpx 14rpx;
    border-radius: 999rpx;
    color: #92400e;
    background: #fef3c7;
  }

  .message-time {
    color: #9ca3af;
  }

  .message-status {
    color: #94a3b8;
  }

  .message-status.unread {
    color: #dc2626;
  }

  .message-title {
    font-size: 32rpx;
    font-weight: 700;
    color: #111827;
  }

  .message-summary {
    font-size: 25rpx;
    line-height: 1.7;
    color: #6b7280;
  }

  .load-more-btn {
    margin-top: 24rpx;
    color: #0f172a;
    background: #ffffff;
    border: 2rpx solid #d1d5db;
  }
}
</style>