重构订单详情双视图: 抽取 useOrderActions + useOrderPaymentCashier 消除重复
OrderDetailView (PC) 与 MobileOrderDetailView 共 13 个 handler + 9 个 computed 逐字重复, 差异只在 UI 反馈原语 (ElMessage vs showToast)、二次确认交互、支付外壳。本次按 usePublishForm 范式抽取共享 composable, 业务逻辑收敛、UI 反馈靠 options 注入。 新增: - useOrderActions: 订单详情业务逻辑 (load/cancel/pay/handoff/confirm/checkout 五件套/dispute 两件套/evidence upload), 持有状态/表单/computed, UI 反馈与二次确认通过 options 注入, 复用 useOrderSnapshot 的纯函数与 useOrderCheckoutSnapshot - useOrderPaymentCashier: 通用支付收银台 (二维码渲染 + 轮询 + App 浏览器跳转兜底), 反馈靠 options 注入, 轮询间隔/二维码宽度/外部跳转参数化 改造: - useMobilePaymentCashier 改为 useOrderPaymentCashier 的薄封装, 注入 vant toast/showDialog + 微信/支付宝内嵌浏览器跳转, 对外 API 名不变 - OrderDetailView 1505→1120, MobileOrderDetailView 1253→896, 视图净减 742 行重复代码 - 修复 PC 缺失的 rejectReason/disputeDescription 非空校验 (现网 PC 遗漏, 与 Mobile 对齐) 关键不变式: - handleRejectCheckout 用现网 createDispute(type:'checkout_dispute') 语义, 未引入死代码 useSettlement 的 counterCheckout bug - 轮询间隔 PC 3000ms / Mobile 2500ms 通过 pollIntervalMs 注入, 行为不变 - 二次确认 Mobile 4 项 (cancel/confirmCheckout/acceptCheckout/cancelDispute) 走 showDialog, PC 直接放行 - autoPay (route.query.pay==='1') 触发逻辑保留 chatPathPrefix 移入 options 并加 onCounterCheckoutSuccess/onRejectCheckoutSuccess/onCreateDisputeSuccess 三个可选 UI 回调: 修复 PC template @click="openOrderChat" 误把 PointerEvent 当路由前缀 (跳成 [object PointerEvent]), 以及 Mobile 修改结账/拒绝修正/提交申诉成功后 popup 不关闭的回归。 vue-tsc / eslint / vite build 全部通过。
This commit is contained in:
@@ -1,145 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ChatDotRound, CopyDocument, Loading } from '@element-plus/icons-vue'
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import QRCode from 'qrcode'
|
||||
|
||||
import { fetchOrderChat } from '@/features/chats/api/chats'
|
||||
import { cancelOrderDispute, createDispute } from '@/features/disputes'
|
||||
import { uploadFile } from '@/shared/api/files'
|
||||
import { readError } from '@/shared/utils/error'
|
||||
import { formatCent } from '@/shared/utils/money'
|
||||
import {
|
||||
acceptCheckout,
|
||||
amountYuan,
|
||||
cancelOrder,
|
||||
confirmCheckout,
|
||||
confirmReceive,
|
||||
counterCheckout,
|
||||
fetchHandoffRecords,
|
||||
fetchOrder,
|
||||
hydrateCounterFormFromCheckout,
|
||||
linesToList,
|
||||
money,
|
||||
OrderCheckoutSummary,
|
||||
OrderHandoffTimeline,
|
||||
OrderResourceUsageEditor,
|
||||
orderEstimatedEndAt as readOrderEstimatedEndAt,
|
||||
orderRentAmount,
|
||||
ownerActualIncome,
|
||||
queryOrderPayment,
|
||||
startOrderPayment,
|
||||
submitCheckout,
|
||||
submitHandoff,
|
||||
type HandoffRecord,
|
||||
type Order,
|
||||
type PaymentOrder,
|
||||
useOrderCheckoutSnapshot,
|
||||
} from '@/features/orders'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import { useOrderActions } from '@/features/orders/composables/useOrderActions'
|
||||
import { useOrderPaymentCashier } from '@/features/orders/composables/useOrderPaymentCashier'
|
||||
import { formatCent } from '@/shared/utils/money'
|
||||
import { handoffStatusLabel, orderStatusLabel } from '@/shared/utils/statusLabels'
|
||||
import { formatDateTime } from '@/shared/utils/time'
|
||||
import { formatListingNo } from '@/shared/utils/listingDisplay'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const session = useSessionStore()
|
||||
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 cancellingDispute = ref(false)
|
||||
const uploadingEvidence = ref(false)
|
||||
const order = ref<Order | null>(null)
|
||||
const handoffRecords = ref<HandoffRecord[]>([])
|
||||
const handoffContent = ref('')
|
||||
const paymentDialogVisible = ref(false)
|
||||
const activePayment = ref<PaymentOrder | null>(null)
|
||||
// 支付收银台:二维码渲染 + 轮询查询。PC 不做 App 浏览器跳转,直接渲染二维码弹窗。
|
||||
const cashier = useOrderPaymentCashier({
|
||||
notifySuccess: message => ElMessage.success(message),
|
||||
notifyError: message => ElMessage.error(message),
|
||||
notifyInfo: message => ElMessage.info(message),
|
||||
notifyFallback: message => ElMessage.warning(message),
|
||||
paidMessage: '支付成功,等待号主交接',
|
||||
pollIntervalMs: 3000,
|
||||
qrWidth: 240,
|
||||
})
|
||||
|
||||
const listingCode = computed(() =>
|
||||
order.value ? formatListingNo(order.value.listing_no, order.value.listing_id) : '-'
|
||||
)
|
||||
const paymentQRCodeURL = ref('')
|
||||
const qrGenerating = ref(false)
|
||||
const checkingPayment = ref(false)
|
||||
let paymentPollingTimer: number | undefined
|
||||
const checkoutForm = ref({
|
||||
content: '',
|
||||
consumableAmountYuan: 0,
|
||||
coin_consumed_m: 0,
|
||||
otherAmountYuan: 0,
|
||||
evidenceText: '',
|
||||
})
|
||||
const resourceUsage = ref<Record<string, number>>({})
|
||||
const counterForm = ref({
|
||||
consumableAmountYuan: 0,
|
||||
coin_consumed_m: 0,
|
||||
otherAmountYuan: 0,
|
||||
depositDeductAmountYuan: 0,
|
||||
reason: '',
|
||||
evidenceText: '',
|
||||
})
|
||||
const rejectReason = ref('')
|
||||
const disputeType = ref('cannot_login')
|
||||
const disputeDescription = ref('')
|
||||
const disputeEvidenceText = ref('')
|
||||
const openingChat = ref(false)
|
||||
let autoPayHandled = false
|
||||
|
||||
const isOwner = computed(() => order.value?.owner_id === session.userId)
|
||||
const isRenter = computed(() => order.value?.renter_id === session.userId)
|
||||
const orderAmountLabel = computed(() => (isOwner.value ? '预计租金' : '支付租金'))
|
||||
const orderRentDisplayAmount = computed(() =>
|
||||
order.value ? orderRentAmount(order.value, session.userId) : 0
|
||||
)
|
||||
const ownerIncomeDisplayAmount = computed(() =>
|
||||
order.value ? ownerActualIncome(order.value, session.userId) : null
|
||||
)
|
||||
const ownerIncomeLabel = computed(() =>
|
||||
order.value?.status === 'completed' ? '实际到手' : '结账预计到手'
|
||||
)
|
||||
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 canCancelDispute = computed(() => {
|
||||
return (
|
||||
!!order.value &&
|
||||
(isOwner.value || isRenter.value) &&
|
||||
['disputing', 'checkout_disputing'].includes(order.value.status) &&
|
||||
order.value.active_dispute?.initiator_id === session.userId
|
||||
)
|
||||
})
|
||||
const isCheckoutDisputeStage = computed(() => {
|
||||
return (
|
||||
!!order.value &&
|
||||
['pending_checkout_confirm', 'pending_checkout_accept'].includes(order.value.status)
|
||||
)
|
||||
})
|
||||
// 订单详情交互:业务逻辑、状态、计算属性、handler。
|
||||
const {
|
||||
loading,
|
||||
cancelling,
|
||||
startingPayment,
|
||||
handoffing,
|
||||
confirming,
|
||||
returning,
|
||||
completing,
|
||||
countering,
|
||||
acceptingCheckout,
|
||||
rejectingCheckout,
|
||||
disputing,
|
||||
cancellingDispute,
|
||||
uploadingEvidence,
|
||||
openingChat,
|
||||
order,
|
||||
handoffRecords,
|
||||
handoffContent,
|
||||
checkoutForm,
|
||||
resourceUsage,
|
||||
counterForm,
|
||||
rejectReason,
|
||||
disputeType,
|
||||
disputeDescription,
|
||||
disputeEvidenceText,
|
||||
checkoutResources,
|
||||
resourceChargeAmount,
|
||||
snapshotHafCoinM,
|
||||
remainingHafCoinM,
|
||||
hydrateResourceUsage,
|
||||
resourceLineAmount,
|
||||
checkoutContentWithSummary,
|
||||
} = useOrderCheckoutSnapshot({ order, checkoutForm, resourceUsage })
|
||||
isOwner,
|
||||
isRenter,
|
||||
listingCode,
|
||||
orderAmountLabel,
|
||||
orderRentDisplayAmount,
|
||||
ownerIncomeDisplayAmount,
|
||||
ownerIncomeLabel,
|
||||
canOpenDispute,
|
||||
canCancelDispute,
|
||||
isCheckoutDisputeStage,
|
||||
handleCancel,
|
||||
handlePay,
|
||||
handleSubmitHandoff,
|
||||
handleConfirmReceive,
|
||||
handleSubmitCheckout,
|
||||
handleConfirmCheckout,
|
||||
handleCounterCheckout,
|
||||
handleAcceptCheckout,
|
||||
handleRejectCheckout,
|
||||
handleCreateDispute,
|
||||
handleCancelDispute,
|
||||
handleEvidenceUpload,
|
||||
openOrderChat,
|
||||
orderEstimatedEndAt,
|
||||
orderRentedAt,
|
||||
} = useOrderActions({
|
||||
notifySuccess: message => ElMessage.success(message),
|
||||
notifyError: message => ElMessage.error(message),
|
||||
notifyWarning: message => ElMessage.warning(message),
|
||||
confirm: async () => true, // PC 不做二次确认,直接放行。
|
||||
actionsNeedingConfirm: [],
|
||||
ordersPath: '/orders',
|
||||
chatPathPrefix: '/messages/',
|
||||
startCashier: (payment, onPaid) => cashier.openPaymentCashier(payment, onPaid),
|
||||
})
|
||||
|
||||
// 支付弹窗别名:保持 template 字段名不变。
|
||||
const paymentDialogVisible = cashier.paymentPopupVisible
|
||||
const activePayment = cashier.activePayment
|
||||
const paymentQRCodeURL = cashier.paymentQRCodeURL
|
||||
const qrGenerating = cashier.qrGenerating
|
||||
const checkingPayment = cashier.checkingPayment
|
||||
const paymentPayURL = cashier.payURL
|
||||
function handleRefreshPayment() {
|
||||
void cashier.refreshPaymentStatus(false)
|
||||
}
|
||||
function stopPaymentPolling() {
|
||||
cashier.stopPaymentPolling()
|
||||
}
|
||||
|
||||
function getOrderStep(status: string) {
|
||||
const stepMap: Record<string, number> = {
|
||||
@@ -156,375 +122,24 @@ function getOrderStep(status: string) {
|
||||
return stepMap[status] ?? 0
|
||||
}
|
||||
|
||||
onMounted(loadOrder)
|
||||
onBeforeUnmount(stopPaymentPolling)
|
||||
|
||||
async function loadOrder() {
|
||||
loading.value = true
|
||||
try {
|
||||
order.value = await fetchOrder(String(route.params.id))
|
||||
handoffRecords.value = await fetchHandoffRecords(String(route.params.id))
|
||||
hydrateResourceUsage()
|
||||
hydrateCounterForm()
|
||||
if (
|
||||
!autoPayHandled &&
|
||||
route.query.pay === '1' &&
|
||||
order.value?.status === 'pending_payment' &&
|
||||
order.value?.renter_id === session.userId
|
||||
) {
|
||||
autoPayHandled = true
|
||||
void router.replace({ path: route.path })
|
||||
void handlePay()
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCancel() {
|
||||
if (!order.value) return
|
||||
cancelling.value = true
|
||||
try {
|
||||
await cancelOrder(order.value.id)
|
||||
ElMessage.success('订单已取消,账号已释放')
|
||||
await router.push('/orders')
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '取消失败'))
|
||||
} finally {
|
||||
cancelling.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePay() {
|
||||
if (!order.value) return
|
||||
startingPayment.value = true
|
||||
try {
|
||||
const payment = await startOrderPayment(order.value.id)
|
||||
if (payment.paid) {
|
||||
ElMessage.success('支付成功,等待号主交接')
|
||||
await loadOrder()
|
||||
} else {
|
||||
showPaymentDialog(payment)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '支付失败'))
|
||||
} finally {
|
||||
startingPayment.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function showPaymentDialog(payment: PaymentOrder) {
|
||||
activePayment.value = payment
|
||||
paymentDialogVisible.value = true
|
||||
void renderPaymentQRCode(payment)
|
||||
startPaymentPolling()
|
||||
}
|
||||
|
||||
function paymentPayURL(payment = activePayment.value) {
|
||||
return payment?.jspay_url || payment?.td_code || payment?.jspay_info || ''
|
||||
}
|
||||
|
||||
async function renderPaymentQRCode(payment = activePayment.value) {
|
||||
const payURL = paymentPayURL(payment)
|
||||
paymentQRCodeURL.value = ''
|
||||
if (!payURL) {
|
||||
return
|
||||
}
|
||||
qrGenerating.value = true
|
||||
try {
|
||||
paymentQRCodeURL.value = await QRCode.toDataURL(payURL, {
|
||||
width: 240,
|
||||
margin: 1,
|
||||
errorCorrectionLevel: 'M',
|
||||
color: {
|
||||
dark: '#111827',
|
||||
light: '#ffffff',
|
||||
},
|
||||
})
|
||||
} catch {
|
||||
ElMessage.error('二维码生成失败')
|
||||
} finally {
|
||||
qrGenerating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function startPaymentPolling() {
|
||||
stopPaymentPolling()
|
||||
paymentPollingTimer = window.setInterval(() => {
|
||||
void refreshPaymentStatus(true)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
function stopPaymentPolling() {
|
||||
if (paymentPollingTimer !== undefined) {
|
||||
window.clearInterval(paymentPollingTimer)
|
||||
paymentPollingTimer = undefined
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshPaymentStatus(silent = false) {
|
||||
if (!order.value || checkingPayment.value) {
|
||||
return
|
||||
}
|
||||
checkingPayment.value = true
|
||||
const currentPayURL = paymentPayURL()
|
||||
try {
|
||||
const payment = await queryOrderPayment(order.value.id)
|
||||
if (payment.paid) {
|
||||
stopPaymentPolling()
|
||||
ElMessage.success('支付成功')
|
||||
paymentDialogVisible.value = false
|
||||
await loadOrder()
|
||||
} else {
|
||||
activePayment.value = payment
|
||||
if (paymentPayURL(payment) !== currentPayURL) {
|
||||
await renderPaymentQRCode(payment)
|
||||
}
|
||||
if (!silent) {
|
||||
ElMessage.info('支付未完成')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (!silent) {
|
||||
ElMessage.error(readError(error, '刷新支付状态失败'))
|
||||
}
|
||||
} finally {
|
||||
checkingPayment.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRefreshPayment() {
|
||||
await refreshPaymentStatus(false)
|
||||
}
|
||||
|
||||
async function handleSubmitHandoff() {
|
||||
if (!order.value) return
|
||||
handoffing.value = true
|
||||
try {
|
||||
await submitHandoff(order.value.id, handoffContent.value)
|
||||
handoffContent.value = ''
|
||||
ElMessage.success('交接说明已提交')
|
||||
await loadOrder()
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '提交交接失败'))
|
||||
} finally {
|
||||
handoffing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleConfirmReceive() {
|
||||
if (!order.value) return
|
||||
confirming.value = true
|
||||
try {
|
||||
await confirmReceive(order.value.id)
|
||||
ElMessage.success('已确认收号,订单进入使用中')
|
||||
await loadOrder()
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '确认收号失败'))
|
||||
} finally {
|
||||
confirming.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmitCheckout() {
|
||||
if (!order.value) return
|
||||
returning.value = true
|
||||
try {
|
||||
const consumableAmount = resourceChargeAmount.value
|
||||
checkoutForm.value.consumableAmountYuan = consumableAmount
|
||||
await submitCheckout(order.value.id, {
|
||||
content: checkoutContentWithSummary(),
|
||||
consumableAmountYuan: consumableAmount,
|
||||
coin_consumed_m: checkoutForm.value.coin_consumed_m,
|
||||
otherAmountYuan: checkoutForm.value.otherAmountYuan,
|
||||
evidence_urls: linesToList(checkoutForm.value.evidenceText),
|
||||
})
|
||||
checkoutForm.value = {
|
||||
content: '',
|
||||
consumableAmountYuan: 0,
|
||||
coin_consumed_m: 0,
|
||||
otherAmountYuan: 0,
|
||||
evidenceText: '',
|
||||
}
|
||||
resourceUsage.value = {}
|
||||
ElMessage.success('结账已发起,等待号主确认')
|
||||
await loadOrder()
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '发起结账失败'))
|
||||
} finally {
|
||||
returning.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleConfirmCheckout() {
|
||||
if (!order.value) return
|
||||
completing.value = true
|
||||
try {
|
||||
await confirmCheckout(order.value.id)
|
||||
ElMessage.success('结账已确认,订单完成')
|
||||
await loadOrder()
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '确认结账失败'))
|
||||
} finally {
|
||||
completing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCounterCheckout() {
|
||||
if (!order.value) return
|
||||
countering.value = true
|
||||
try {
|
||||
const reasonText = counterForm.value.reason.trim()
|
||||
await counterCheckout(order.value.id, {
|
||||
content: reasonText,
|
||||
consumableAmountYuan: counterForm.value.consumableAmountYuan,
|
||||
coin_consumed_m: counterForm.value.coin_consumed_m,
|
||||
otherAmountYuan: counterForm.value.otherAmountYuan,
|
||||
depositDeductAmountYuan: counterForm.value.depositDeductAmountYuan,
|
||||
reason: reasonText,
|
||||
evidence_urls: linesToList(counterForm.value.evidenceText),
|
||||
})
|
||||
ElMessage.success('结账修正已提交,等待租客确认')
|
||||
await loadOrder()
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '修改结账失败'))
|
||||
} finally {
|
||||
countering.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAcceptCheckout() {
|
||||
if (!order.value) return
|
||||
acceptingCheckout.value = true
|
||||
try {
|
||||
await acceptCheckout(order.value.id)
|
||||
ElMessage.success('已确认修正结账,订单完成')
|
||||
await loadOrder()
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '确认修正失败'))
|
||||
} finally {
|
||||
acceptingCheckout.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRejectCheckout() {
|
||||
if (!order.value) return
|
||||
rejectingCheckout.value = true
|
||||
try {
|
||||
await createDispute(order.value.id, {
|
||||
type: 'checkout_dispute',
|
||||
description: rejectReason.value,
|
||||
evidence_urls: [],
|
||||
})
|
||||
rejectReason.value = ''
|
||||
ElMessage.success('已拒绝修正结账,订单进入争议处理')
|
||||
await loadOrder()
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '拒绝修正失败'))
|
||||
} finally {
|
||||
rejectingCheckout.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCreateDispute() {
|
||||
if (!order.value) return
|
||||
disputing.value = true
|
||||
try {
|
||||
const evidence_urls = linesToList(disputeEvidenceText.value)
|
||||
await createDispute(order.value.id, {
|
||||
type: isCheckoutDisputeStage.value ? 'checkout_dispute' : disputeType.value,
|
||||
description: disputeDescription.value,
|
||||
evidence_urls,
|
||||
})
|
||||
disputeDescription.value = ''
|
||||
disputeEvidenceText.value = ''
|
||||
ElMessage.success('申诉已提交,订单进入仲裁处理')
|
||||
await loadOrder()
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '提交申诉失败'))
|
||||
} finally {
|
||||
disputing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCancelDispute() {
|
||||
if (!order.value) return
|
||||
cancellingDispute.value = true
|
||||
try {
|
||||
await cancelOrderDispute(order.value.id)
|
||||
ElMessage.success('申诉已取消,订单已恢复')
|
||||
await loadOrder()
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '取消申诉失败'))
|
||||
} finally {
|
||||
cancellingDispute.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleEvidenceUpload(event: Event) {
|
||||
const input = event.target as HTMLInputElement
|
||||
const file = input.files?.[0]
|
||||
input.value = ''
|
||||
if (!file) return
|
||||
uploadingEvidence.value = true
|
||||
try {
|
||||
const uploaded = await uploadFile(file, 'dispute')
|
||||
disputeEvidenceText.value = [disputeEvidenceText.value, uploaded.url].filter(Boolean).join('\n')
|
||||
ElMessage.success('证据文件已上传')
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '上传失败'))
|
||||
} finally {
|
||||
uploadingEvidence.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function openOrderChat() {
|
||||
if (!order.value || openingChat.value) return
|
||||
openingChat.value = true
|
||||
try {
|
||||
const chat = await fetchOrderChat(order.value.id)
|
||||
await router.push(`/messages/${chat.id}`)
|
||||
} catch {
|
||||
ElMessage.error('订单群聊暂不可用')
|
||||
} finally {
|
||||
openingChat.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function orderEstimatedEndAt() {
|
||||
return readOrderEstimatedEndAt(order.value)
|
||||
}
|
||||
|
||||
function orderRentedAt() {
|
||||
return order.value?.rented_at
|
||||
}
|
||||
|
||||
function hydrateCounterForm() {
|
||||
if (!order.value?.checkout) return
|
||||
counterForm.value = hydrateCounterFormFromCheckout(order.value.checkout)
|
||||
}
|
||||
|
||||
function scrollToDispute() {
|
||||
const disputeSection = document.querySelector('.dispute-section')
|
||||
if (disputeSection) {
|
||||
disputeSection.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToCheckout() {
|
||||
const checkoutSection = document.querySelector('.checkout-form-section')
|
||||
if (checkoutSection) {
|
||||
checkoutSection.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToCounterCheckout() {
|
||||
const counterSection = document.querySelector('.counter-checkout-section')
|
||||
if (counterSection) {
|
||||
counterSection.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToRejectCheckout() {
|
||||
const rejectSection = document.querySelector('.reject-checkout-section')
|
||||
if (rejectSection) {
|
||||
@@ -543,6 +158,7 @@ async function copyListingCode() {
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<section class="page order-detail-page" v-loading="loading">
|
||||
<div v-if="order" class="page-header">
|
||||
@@ -973,7 +589,7 @@ async function copyListingCode() {
|
||||
<strong>¥{{ formatCent(activePayment.amount_cent) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="paymentPayURL()" class="pay-qr-section">
|
||||
<div v-if="paymentPayURL" class="pay-qr-section">
|
||||
<div class="pay-qr-box">
|
||||
<el-icon v-if="qrGenerating" class="is-loading" :size="32"><Loading /></el-icon>
|
||||
<img v-else-if="paymentQRCodeURL" :src="paymentQRCodeURL" alt="支付二维码" />
|
||||
|
||||
Reference in New Issue
Block a user