index.vue 5.04 KB
<!--
 * @Date: 2026-02-05
 * @Description: 我的收藏 - 已接入真实API,移除分类逻辑
-->
<template>
  <view class="h-screen bg-gray-50 flex flex-col">
    <view class="bg-gray-50 z-10">
      <NavHeader title="我的收藏" />
    </view>

    <view
      v-if="listVisible"
      :key="listRenderKey"
      class="flex-1 min-h-0 overflow-y-auto px-[24rpx] py-[24rpx] pb-[200rpx]"
    >
      <!-- Loading State -->
      <view v-if="loading" class="flex flex-col items-center justify-center">
        <view class="loading-spinner"></view>
        <view class="text-gray-400 text-[24rpx] mt-3">加载中...</view>
      </view>

      <!-- List Items -->
      <view v-for="(item, index) in favoritesList" :key="item.meta_id"
        class="bg-white rounded-[24rpx] p-[24rpx] mb-[24rpx] shadow-sm favorite-item"
        :style="{ animationDelay: `${index * 50}ms` }">

        <!-- Header with Icon -->
        <view class="flex gap-[24rpx] mb-[12rpx]">
          <!-- Document Icon -->
          <view class="w-[88rpx] h-[88rpx] mr-[24rpx] flex-shrink-0 flex items-center justify-center bg-gradient-to-br from-blue-50 to-blue-100 rounded-[20rpx] shadow-inner self-start">
            <image :src="getDocumentIcon(item.name)" class="w-[48rpx] h-[48rpx]" mode="aspectFit" />
          </view>

          <!-- Title -->
          <view class="flex-1 min-w-0">
            <view class="text-[30rpx] font-bold text-gray-900 leading-normal mb-1">{{ item.name }}</view>
            <view class="text-gray-400 text-[22rpx]">{{ item.size }}</view>
          </view>
        </view>

        <!-- Date -->
        <view class="text-gray-500 text-[24rpx] mb-[20rpx] text-right">
          <text>{{ item.created_time }}</text>
        </view>

        <!-- Divider -->
        <view class="h-[1rpx] bg-gray-100 mb-[20rpx]"></view>

        <!-- Actions -->
        <ListItemActions
          :viewable="true"
          :deletable="true"
          :item-id="String(item.meta_id)"
          @view="viewFile({...item, fileName: item.name, downloadUrl: item.src})"
          @delete="onDelete(item)"
        />
      </view>

      <!-- Empty State -->
      <view v-if="!loading && favoritesList.length === 0">
        <nut-empty description="暂无收藏内容" image="empty" />
      </view>
    </view>

    <!-- TabBar -->
    <!-- <TabBar current="me" /> -->
  </view>
</template>

<script setup>
import { ref } from 'vue'
import Taro from '@tarojs/taro'
import { useFileOperation } from '@/composables/useFileOperation'
import { getDocumentIcon } from '@/utils/documentIcons'
import NavHeader from '@/components/NavHeader.vue'
import ListItemActions from '@/components/ListItemActions/index.vue'
import { listAPI, delAPI } from '@/api/favorite'

const { viewFile } = useFileOperation()
const listVisible = ref(true)
const listRenderKey = ref(0)
const loading = ref(false)
const favoritesList = ref([])

/**
 * 获取收藏列表
 */
const fetchFavoritesList = async () => {
  try {
    loading.value = true
    const res = await listAPI({
      page: '0',
      limit: '100'
    })

    if (res.code === 1 && res.data && res.data.list) {
      favoritesList.value = res.data.list
    } else {
      favoritesList.value = []
      Taro.showToast({
        title: res.msg || '获取收藏列表失败',
        icon: 'none'
      })
    }
  } catch (err) {
    console.error('获取收藏列表失败:', err)
    favoritesList.value = []
    Taro.showToast({
      title: '网络错误,请稍后重试',
      icon: 'none'
    })
  } finally {
    loading.value = false
  }
}

/**
 * 删除收藏
 */
const onDelete = async (item) => {
  Taro.showModal({
    title: '提示',
    content: '确定要删除该收藏吗?',
    success: async (res) => {
      if (res.confirm) {
        try {
          const delRes = await delAPI({ meta_id: item.meta_id })

          if (delRes.code === 1) {
            // 从列表中移除
            const index = favoritesList.value.findIndex(i => i.meta_id === item.meta_id)
            if (index !== -1) {
              favoritesList.value.splice(index, 1)
            }

            Taro.showToast({ title: '已删除', icon: 'success' })
          } else {
            Taro.showToast({
              title: delRes.msg || '删除失败',
              icon: 'none'
            })
          }
        } catch (err) {
          console.error('删除收藏失败:', err)
          Taro.showToast({
            title: '网络错误,请稍后重试',
            icon: 'none'
          })
        }
      }
    }
  })
}

// 获取收藏列表
fetchFavoritesList()
</script>

<style lang="less">
@keyframes slideIn {
  from {
    opacity: 0;
    transform: translateY(20rpx);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}

.favorite-item {
  animation: slideIn 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) backwards;
}

.loading-spinner {
  width: 64rpx;
  height: 64rpx;
  border: 4rpx solid #e5e7eb;
  border-top-color: #2563EB;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}
</style>