usePlanView.js 4.42 KB
/**
 * 计划书查看 Composable
 *
 * @description 封装计划书查看逻辑,支持:
 * - 单文件直接预览
 * - 多文件显示选择弹框
 * - 预览成功后标记为已查看
 * - 传入 proposal 数据自动处理状态和文件
 *
 * @example
 * const { viewProposal } = usePlanView()
 *
 * // 方式1:传入完整的 proposal 对象(从消息详情 API 获取)
 * viewProposal({
 *   id: 123,
 *   order_status: '7',
 *   proposal_files: [
 *     { file_name: '计划书.pdf', file_url: 'xxx', id: 1 }
 *   ]
 * })
 *
 * // 方式2:传入已转换的 item(从计划书列表获取)
 * viewProposal(planItem)
 *
 * @author Claude Code
 * @version 1.0.0
 */

import { useFileOperation } from './useFileOperation'
import { viewAPI } from '@/api/plan'
import { ORDER_STATUS, mapOrderStatus, getStatusText } from '@/config/constants/orderStatus'
import Taro from '@tarojs/taro'

/**
 * 计划书查看 Hook
 *
 * @returns {Object} 包含 viewProposal 方法的对象
 */
export function usePlanView() {
  const { viewFile } = useFileOperation()

  /**
   * 查看计划书
   *
   * @param {Object} proposal - 计划书对象(支持两种格式)
   * @param {number} proposal.id - 计划书 ID(必需)
   * @param {string} proposal.order_status - 订单状态(API 格式:'3'|'5'|'7'|'9')
   * @param {Array} proposal.proposal_files - 文件列表(API 格式)
   * @param {string} proposal.status - 订单状态(前端格式,兼容列表数据)
   * @param {Array} proposal.proposalFiles - 文件列表(兼容列表数据)
   * @param {Object} callbacks - 回调函数
   * @param {Function} callbacks.onViewSuccess - 查看成功后回调,参数为 proposalId
   * @param {Function} callbacks.beforeView - 查看前回调,返回 false 可取消查看
   * @returns {Promise<void>}
   */
  const viewProposal = async (proposal, callbacks = {}) => {
    const { beforeView, onViewSuccess } = callbacks

    // 1. 状态检查 - 解析两种可能的状态字段
    const status = proposal.status || mapOrderStatus(proposal.order_status)

  if (status === ORDER_STATUS.PENDING || status === ORDER_STATUS.PROCESSING) {
      Taro.showToast({
        title: '计划书尚未生成,请稍后',
        icon: 'none'
      })
      return
    }

    // 2. 解析文件列表 - 支持两种可能的字段名
    const proposalFiles = proposal.proposal_files || proposal.proposalFiles || []

    if (!proposalFiles || proposalFiles.length === 0) {
      Taro.showToast({
        title: '暂无可查看的计划书',
        icon: 'none'
      })
      return
    }

    // 3. 执行查看前回调
    if (beforeView) {
      const shouldContinue = await beforeView(proposal)
      if (shouldContinue === false) return
    }

    /**
     * 处理单个文件的查看
     *
     * @param {Object} file - 文件对象
     * @param {string} file.file_url - 文件 URL
     * @param {string} file.file_name - 文件名称
     */
    const handleFileView = async (file) => {
      try {
        const previewSuccess = await viewFile({
          downloadUrl: file.file_url,
          fileName: file.file_name
        })

        if (!previewSuccess) return

        // 4. 预览成功后标记为已查看
        if (status !== 'viewed' && proposal.id) {
          const viewRes = await viewAPI({ i: proposal.id })

          if (viewRes.code === 1) {
            Taro.showToast({
              title: '已标记为查看',
              icon: 'success',
              duration: 1000
            })

            // 触发成功回调
            if (onViewSuccess) {
              onViewSuccess(proposal.id)
            }
          }
        }
      } catch (error) {
        console.error('查看计划书文件失败:', error)
      }
    }

    // 5. 单文件直接查看
    if (proposalFiles.length === 1) {
      await handleFileView(proposalFiles[0])
      return
    }

    // 6. 多文件显示选择弹框
    const fileList = proposalFiles.map((file, index) => ({
      text: file.file_name || `计划书 ${index + 1}`,
      file: file
    }))

    Taro.showActionSheet({
      itemList: fileList.map(f => f.text),
      success: async (res) => {
        const selectedIndex = res.tapIndex
        if (selectedIndex !== undefined && selectedIndex >= 0) {
          const selectedFile = fileList[selectedIndex].file
          await handleFileView(selectedFile)
        }
      }
    })
  }

  return {
    viewProposal,
    mapOrderStatus,
    getStatusText
  }
}