useListItemClick.js 6.08 KB
/**
 * 统一的列表项点击处理 Composable
 *
 * @description 根据列表类型和上下文,智能处理列表项的点击行为
 * 支持文件预览、页面跳转、弹窗显示等多种交互模式
 *
 * @author Claude Code
 * @date 2026-01-31
 */

import { useGo } from '@/hooks/useGo'
import { useFileOperation } from './useFileOperation'

/**
 * 列表类型枚举
 *
 * @description 定义不同列表的点击行为类型
 * @enum {string}
 */
export const ListType = {
  /** 文件列表 - 点击打开文件预览 */
  FILE: 'file',
  /** 产品列表 - 点击跳转产品详情 */
  PRODUCT: 'product',
  /** 搜索结果 - 根据类型智能路由 */
  SEARCH: 'search',
  /** 帮助中心 - 点击显示弹窗 */
  HELP: 'help',
  /** 收藏列表 - 点击打开文件 + 长按删除 */
  FAVORITE: 'favorite'
}

/**
 * 列表项点击处理 Hook
 *
 * @param {Object} options - 配置选项
 * @param {string} options.listType - 列表类型(使用 ListType 枚举)
 * @param {Function} [options.onBeforeClick] - 点击前的回调函数,返回 false 可阻止默认行为
 * @param {Function} [options.onAfterClick] - 点击后的回调函数
 * @returns {Object} 点击处理方法
 *
 * @example
 * // 文件列表
 * const { handleClick } = useListItemClick({ listType: ListType.FILE })
 *
 * // 产品列表
 * const { handleClick } = useListItemClick({
 *   listType: ListType.PRODUCT,
 *   onAfterClick: (item) => console.log('Viewed product:', item.id)
 * })
 *
 * // 自定义逻辑
 * const { handleClick } = useListItemClick({
 *   listType: ListType.FILE,
 *   onBeforeClick: (item) => {
 *     if (!item.hasPermission) {
 *       Taro.showToast({ title: '无权限', icon: 'none' })
 *       return false
 *     }
 *   }
 * })
 */
export function useListItemClick(options = {}) {
  const { listType = ListType.FILE, onBeforeClick, onAfterClick } = options

  const go = useGo()
  const { viewFile } = useFileOperation()

  /**
   * 处理文件列表项点击
   *
   * @description 打开文件预览
   * @async
   * @param {Object} item - 列表项数据
   * @param {string} item.downloadUrl - 文件下载地址
   * @param {string} item.fileName - 文件名
   */
  const handleFileClick = async (item) => {
    await viewFile(item)
  }

  /**
   * 处理产品列表项点击
   *
   * @description 跳转到产品详情页
   * @param {Object} item - 列表项数据
   * @param {number|string} item.id - 产品ID
   */
  const handleProductClick = (item) => {
    if (!item.id) {
      console.warn('Product item missing id:', item)
      return
    }
    go('/pages/product-detail/index', { id: item.id })
  }

  /**
   * 处理搜索结果项点击
   *
   * @description 根据搜索结果类型智能路由
   * @param {Object} item - 列表项数据
   * @param {string} item.type - 结果类型(product/material/course等)
   * @param {number|string} item.id - 资源ID
   */
  const handleSearchClick = (item) => {
    const { type, id } = item

    if (!type) {
      console.warn('Search item missing type:', item)
      return
    }

    // 根据类型路由到不同页面
    switch (type) {
      case 'product':
        if (id) go('/pages/product-detail/index', { id })
        break
      case 'material':
        if (item.downloadUrl) {
          viewFile(item)
        } else if (id) {
          go('/pages/material-detail/index', { id })
        }
        break
      case 'course':
        if (id) go('/pages/course-detail/index', { id })
        break
      default:
        console.warn('Unknown search result type:', type)
    }
  }

  /**
   * 处理帮助中心项点击
   *
   * @description 显示帮助内容弹窗
   * @param {Object} item - 列表项数据
   * @param {string} item.title - 帮助标题
   * @param {string} item.content - 帮助内容
   */
  const handleHelpClick = (item) => {
    if (!item.title && !item.content) {
      console.warn('Help item missing content:', item)
      return
    }

    // 这里需要引入 Taro 的 showModal
    // 实际使用时从 @tarojs/taro 引入
    import('@tarojs/taro').then(({ showModal }) => {
      showModal({
        title: item.title || '提示',
        content: item.content || '暂无内容',
        showCancel: false,
        confirmText: '我知道了'
      })
    })
  }

  /**
   * 统一的点击处理函数
   *
   * @description 根据列表类型分发到不同的处理逻辑
   * @async
   * @param {Object} item - 列表项数据
   * @param {Object} [event] - 点击事件对象(可选,用于阻止事件冒泡)
   *
   * @example
   * // 在模板中使用
   * <view @click="handleClick(item)">点击我</view>
   *
   * // 阻止事件冒泡
   * <view @click.stop="handleClick(item)">点击我</view>
   */
  const handleClick = async (item, event) => {
    // 执行点击前回调
    if (onBeforeClick) {
      const shouldContinue = await onBeforeClick(item)
      if (shouldContinue === false) {
        return
      }
    }

    // 根据列表类型处理点击
    switch (listType) {
      case ListType.FILE:
        await handleFileClick(item)
        break
      case ListType.PRODUCT:
        handleProductClick(item)
        break
      case ListType.SEARCH:
        handleSearchClick(item)
        break
      case ListType.HELP:
        handleHelpClick(item)
        break
      case ListType.FAVORITE:
        // 收藏列表默认也是打开文件
        await handleFileClick(item)
        break
      default:
        console.warn('Unknown list type:', listType)
    }

    // 执行点击后回调
    if (onAfterClick) {
      onAfterClick(item)
    }
  }

  /**
   * 处理收藏操作(独立的收藏功能)
   *
   * @description 用于列表项中的收藏按钮点击
   * @param {Object} item - 列表项数据
   * @param {boolean} item.collected - 是否已收藏
   * @param {Function} [onToggle] - 自定义收藏切换回调
   */
  const handleFavorite = (item, onToggle) => {
    // 切换收藏状态
    if (onToggle) {
      onToggle(item)
    } else {
      // 默认行为:直接切换状态
      item.collected = !item.collected
    }
  }

  return {
    handleClick,
    handleFavorite
  }
}