useCollectOperation.js 3.28 KB
/**
 * 收藏操作 Composable
 *
 * @description 统一的收藏/取消收藏逻辑
 * @author Claude Code
 * @created 2026-02-05
 * @updated 2026-02-27 - 改为事件驱动模式,移除乐观更新
 */

import { addAPI, delAPI } from '../api/favorite.js'
import Taro from '@tarojs/taro'
import eventBus, { Events } from '@/utils/eventBus'

/**
 * 使用收藏操作
 *
 * @description 提供统一的收藏/取消收藏功能
 * @param {Object} options - 配置选项
 * @param {Function} [options.onSuccess] - 成功回调
 * @param {Function} [options.onError] - 错误回调
 * @returns {Object} 收藏操作方法
 *
 * @example
 * const { toggleCollect } = useCollectOperation()
 *
 * const result = await toggleCollect({ id: 123, collected: false })
 * if (result.success) {
 *   // 更新本地状态
 *   item.collected = result.newStatus
 * }
 */
export function useCollectOperation(options = {}) {
  const { onSuccess, onError } = options

  /**
   * 切换收藏状态
   * @description 统一的收藏/取消收藏操作
   * @param {Object} item - 资料项(必须包含 collected 和 id/meta_id 字段)
   * @param {string} successMsg - 成功提示文案(可选)
   * @param {string} [errorMsg='操作失败'] - 失败提示文案
   * @returns {Promise<Object>} 操作结果 { success: boolean, newStatus: boolean }
   */
  const toggleCollect = async (item, successMsg, errorMsg = '操作失败') => {
    // 计算目标状态(注意:这里不修改原对象)
    const newCollectStatus = !item.collected

    // 获取 meta_id(优先使用 meta_id,其次使用 id)
    const metaId = item.meta_id || item.id

    try {
      // 调用 API(根据当前状态决定是添加还是删除)
      const res = newCollectStatus
        ? await addAPI({ meta_id: metaId })  // 添加收藏
        : await delAPI({ meta_id: metaId })  // 取消收藏

      if (res.code === 1) {
        // API 调用成功,显示提示
        Taro.showToast({
          title: successMsg || (newCollectStatus ? '已收藏' : '已取消收藏'),
          icon: 'success',
          duration: 1000
        })

        // 发送收藏更新事件(通知收藏列表页刷新)
        eventBus.emit(Events.FAVORITES_UPDATE, {
          metaId,
          collected: newCollectStatus,
          timestamp: Date.now()
        })

        // 调用成功回调
        onSuccess?.(item, newCollectStatus)

        // 返回成功结果和新状态,由调用方决定如何更新 UI
        return { success: true, newStatus: newCollectStatus }
      } else {
        // API 调用失败
        Taro.showToast({
          title: res.msg || errorMsg,
          icon: 'none',
          duration: 2000
        })

        // 调用错误回调
        onError?.(item, res.msg)

        // 返回失败结果,状态不变
        return { success: false, newStatus: item.collected }
      }
    } catch (err) {
      // 发生错误
      console.error('[useCollectOperation] 收藏操作失败:', err)
      Taro.showToast({
        title: '网络错误,请重试',
        icon: 'none',
        duration: 2000
      })

      // 调用错误回调
      onError?.(item, err.message)

      // 返回失败结果,状态不变
      return { success: false, newStatus: item.collected }
    }
  }

  return {
    toggleCollect
  }
}