import { computed, ref, onMounted, onBeforeUnmount, type Ref } from 'vue' import { useRoute, useRouter } from 'vue-router' import type { Order, HandoffRecord, PaymentOrder, } from '@/api/orders' import { fetchOrder, fetchHandoffRecords, cancelOrder, startOrderPayment, queryOrderPayment, submitHandoff, confirmReceive, submitCheckout, acceptCheckout, counterCheckout, confirmCheckout, } from '@/api/orders' import { createDispute } from '@/api/disputes' import { uploadFile } from '@/api/files' import { fetchOrderChat } from '@/api/chats' import { useSessionStore } from '@/stores/session' export interface CheckoutForm { content: string consumable_amount: number coin_consumed_m: number other_amount: number evidenceText: string } export interface CounterForm { consumable_amount: number coin_consumed_m: number other_amount: number deposit_deduct_amount: number reason: string evidenceText: string } export function useOrderDetail() { const route = useRoute() const router = useRouter() const session = useSessionStore() // Loading states const loading = ref(false) const cancelling = ref(false) const startingPayment = ref(false) const handoffing = ref(false) const confirming = ref(false) const returning = ref(false) const completing = ref(false) const countering = ref(false) const acceptingCheckout = ref(false) const rejectingCheckout = ref(false) const disputing = ref(false) const uploadingEvidence = ref(false) const openingChat = ref(false) // Data const order = ref(null) const handoffRecords = ref([]) const handoffContent = ref('') // Payment states const activePayment = ref(null) const paymentQRCodeURL = ref('') const qrGenerating = ref(false) const checkingPayment = ref(false) let paymentPollingTimer: number | undefined let autoPayHandled = false // Forms const checkoutForm = ref({ content: '', consumable_amount: 0, coin_consumed_m: 0, other_amount: 0, evidenceText: '', }) const resourceUsage = ref>({}) const counterForm = ref({ consumable_amount: 0, coin_consumed_m: 0, other_amount: 0, deposit_deduct_amount: 0, reason: '', evidenceText: '', }) const rejectReason = ref('') const disputeType = ref('cannot_login') const disputeDescription = ref('') const disputeEvidenceText = ref('') // Computed const isOwner = computed(() => order.value?.owner_id === session.userId) const isRenter = computed(() => order.value?.renter_id === session.userId) const orderAmountLabel = computed(() => (isOwner.value ? '我的租金' : '订单金额')) const canOpenDispute = computed(() => { if (!order.value || (!isOwner.value && !isRenter.value)) return false return !['completed', 'cancelled', 'closed', 'disputing', 'checkout_disputing', 'abnormal'].includes( order.value.status ) }) const isCheckoutDisputeStage = computed(() => { return !!order.value && ['pending_checkout_confirm', 'pending_checkout_accept'].includes(order.value.status) }) // Methods async function loadOrder() { loading.value = true try { order.value = await fetchOrder(String(route.params.id)) handoffRecords.value = await fetchHandoffRecords(String(route.params.id)) return order.value } finally { loading.value = false } } async function handleCancel() { if (!order.value) return cancelling.value = true try { await cancelOrder(order.value.id) return true } finally { cancelling.value = false } } async function handlePay() { if (!order.value) return null startingPayment.value = true try { const payment = await startOrderPayment(order.value.id) return payment } finally { startingPayment.value = false } } async function handleSubmitHandoff() { if (!order.value || !handoffContent.value.trim()) return handoffing.value = true try { await submitHandoff(order.value.id, handoffContent.value.trim()) handoffContent.value = '' await loadOrder() return true } finally { handoffing.value = false } } async function handleConfirmReceive() { if (!order.value) return confirming.value = true try { await confirmReceive(order.value.id) await loadOrder() return true } finally { confirming.value = false } } async function handleSubmitCheckout() { if (!order.value) return returning.value = true try { await submitCheckout(order.value.id, { content: checkoutForm.value.content.trim(), consumable_amount: checkoutForm.value.consumable_amount, coin_consumed_m: checkoutForm.value.coin_consumed_m, other_amount: checkoutForm.value.other_amount, evidence: checkoutForm.value.evidenceText.trim(), }) await loadOrder() return true } finally { returning.value = false } } async function handleAcceptCheckout() { if (!order.value) return acceptingCheckout.value = true try { await acceptCheckout(order.value.id) await loadOrder() return true } finally { acceptingCheckout.value = false } } async function handleCounterCheckout() { if (!order.value) return countering.value = true try { await counterCheckout(order.value.id, { consumable_amount: counterForm.value.consumable_amount, coin_consumed_m: counterForm.value.coin_consumed_m, other_amount: counterForm.value.other_amount, deposit_deduct_amount: counterForm.value.deposit_deduct_amount, reason: counterForm.value.reason.trim(), evidence: counterForm.value.evidenceText.trim(), }) await loadOrder() return true } finally { countering.value = false } } async function handleRejectCheckout(reason: string) { if (!order.value) return rejectingCheckout.value = true try { await counterCheckout(order.value.id, { consumable_amount: 0, coin_consumed_m: 0, other_amount: 0, deposit_deduct_amount: 0, reason: reason.trim(), evidence: '', }) await loadOrder() return true } finally { rejectingCheckout.value = false } } async function handleConfirmCheckout() { if (!order.value) return completing.value = true try { await confirmCheckout(order.value.id) await loadOrder() return true } finally { completing.value = false } } async function handleCreateDispute() { if (!order.value) return disputing.value = true try { await createDispute({ order_id: order.value.id, type: disputeType.value, description: disputeDescription.value.trim(), evidence: disputeEvidenceText.value.trim(), }) await loadOrder() return true } finally { disputing.value = false } } async function handleUploadEvidence(file: File) { uploadingEvidence.value = true try { const result = await uploadFile(file) return result.url } finally { uploadingEvidence.value = false } } async function handleOpenChat() { if (!order.value) return openingChat.value = true try { const chat = await fetchOrderChat(order.value.id) return chat } finally { openingChat.value = false } } function startPaymentPolling() { stopPaymentPolling() paymentPollingTimer = window.setInterval(checkPaymentStatus, 2000) } function stopPaymentPolling() { if (paymentPollingTimer !== undefined) { clearInterval(paymentPollingTimer) paymentPollingTimer = undefined } } async function checkPaymentStatus() { if (!activePayment.value || checkingPayment.value) return checkingPayment.value = true try { const updated = await queryOrderPayment(activePayment.value.id) if (updated.paid) { stopPaymentPolling() activePayment.value = null await loadOrder() return true } } catch { // ignore } finally { checkingPayment.value = false } return false } function getOrderStep(status: string) { const stepMap: Record = { pending_payment: 0, pending_handoff: 1, renting: 2, overdue: 2, pending_checkout_confirm: 3, pending_checkout_accept: 3, completed: 4, cancelled: 0, closed: 4, } return stepMap[status] ?? 0 } onMounted(loadOrder) onBeforeUnmount(stopPaymentPolling) return { // States loading, cancelling, startingPayment, handoffing, confirming, returning, completing, countering, acceptingCheckout, rejectingCheckout, disputing, uploadingEvidence, openingChat, // Data order, handoffRecords, handoffContent, // Payment activePayment, paymentQRCodeURL, qrGenerating, checkingPayment, // Forms checkoutForm, resourceUsage, counterForm, rejectReason, disputeType, disputeDescription, disputeEvidenceText, // Computed isOwner, isRenter, orderAmountLabel, canOpenDispute, isCheckoutDisputeStage, // Methods loadOrder, handleCancel, handlePay, handleSubmitHandoff, handleConfirmReceive, handleSubmitCheckout, handleAcceptCheckout, handleCounterCheckout, handleRejectCheckout, handleConfirmCheckout, handleCreateDispute, handleUploadEvidence, handleOpenChat, startPaymentPolling, stopPaymentPolling, checkPaymentStatus, getOrderStep, } }