a7399e0624
hjc-web 此前**没有收银台**——支付步骤内嵌在购标流程(BuyDocument 的 step='pay')里, 买家一旦离开就再也回不到未完成的支付。本提交补上按订单号付款的独立页面。 - 新建 /tender/pay 收银台:只认订单号,金额取自**订单本身**(by-no)而非支付响应 (支付一失败页面就会停在 0.00);进入先判状态,已支付/已取消/已退款**不发**统一下单, 就地显示结论——既无意义,也会多撞一次跨端 OUT_TRADE_NO_USED - 订单列表:待支付卡片加两个按钮 + 手搓 Tailwind 二次确认弹窗(照 ConsultDialog 的风格, 不用 antd 的 Modal:买家页无先例,视觉与站点自建风格不一致) - 状态口径改读 orderStatus(原先只看 payStatus,取消后会把已取消显示成「待支付」 并带上「去支付」),抽 app/utils/orderStatus.ts,与 hjc-h5 刻意保持同一套优先级 - 新增两个 Nitro 代理 order-by-no.get.ts / cancel.post.ts,照既有 my-orders / pay-status 的范式原样透传 ApiResult - 标书详情「购买人数」改读 buyerCount(后端实时统计的已付款订单数) - 跨端 OUT_TRADE_NO_USED:识别后端回带的 data.wechatCode 并给可行动提示, 不匹配 message 字符串(包装方式一改就会静默失效)
129 lines
3.8 KiB
Vue
129 lines
3.8 KiB
Vue
<template>
|
||
<ClientOnly>
|
||
<Teleport to="body">
|
||
<Transition name="hjc-confirm-fade">
|
||
<div
|
||
v-if="open"
|
||
class="fixed inset-0 z-[110] flex items-center justify-center p-4"
|
||
@click.self="onCancel"
|
||
>
|
||
<!-- 遮罩 -->
|
||
<div class="absolute inset-0 bg-black/50" />
|
||
|
||
<!-- 弹窗卡片:照 ConsultDialog.vue 的手搓 Tailwind 风格,不用 antd 的 Modal
|
||
(ant-design-vue 虽已装且自动导入,但买家页没有先例,视觉与站点自建风格不一致) -->
|
||
<Transition name="hjc-confirm-pop" appear>
|
||
<div
|
||
v-if="open"
|
||
class="relative w-full max-w-sm rounded-2xl bg-white p-6 shadow-2xl"
|
||
role="dialog"
|
||
aria-modal="true"
|
||
:aria-label="title"
|
||
>
|
||
<h2 class="text-lg font-bold text-gray-900">{{ title }}</h2>
|
||
<p v-if="content" class="mt-2 text-sm leading-relaxed text-gray-600">{{ content }}</p>
|
||
|
||
<div class="mt-6 flex justify-end gap-3">
|
||
<button
|
||
type="button"
|
||
class="rounded-md border border-gray-300 px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 disabled:opacity-50"
|
||
:disabled="loading"
|
||
@click="onCancel"
|
||
>
|
||
{{ cancelText }}
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="rounded-md bg-blue-600 px-4 py-2 text-sm text-white hover:bg-blue-700 disabled:opacity-50"
|
||
:disabled="loading"
|
||
@click="emit('confirm')"
|
||
>
|
||
{{ loading ? loadingText : confirmText }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</Transition>
|
||
</div>
|
||
</Transition>
|
||
</Teleport>
|
||
</ClientOnly>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
/**
|
||
* 通用二次确认弹窗(破坏性操作用)。
|
||
*
|
||
* 与 ConsultDialog.vue 同款实现路径:`ClientOnly` + `Teleport to="body"` + 手搓 Tailwind。
|
||
* 组件本身不持有任何业务状态——开合由父组件通过 `open` 控制,结果通过 `confirm`/`cancel` 事件回传,
|
||
* 这样"点遮罩不发请求"这类行为天然成立(父组件只在 confirm 里发请求)。
|
||
*/
|
||
const props = withDefaults(defineProps<{
|
||
open: boolean
|
||
title: string
|
||
content?: string
|
||
confirmText?: string
|
||
cancelText?: string
|
||
loadingText?: string
|
||
/** 确认动作进行中:两个按钮都禁用,防连点 */
|
||
loading?: boolean
|
||
}>(), {
|
||
content: '',
|
||
confirmText: '确定',
|
||
cancelText: '再想想',
|
||
loadingText: '处理中...',
|
||
loading: false
|
||
})
|
||
|
||
const emit = defineEmits<{ confirm: []; cancel: [] }>()
|
||
|
||
function onCancel() {
|
||
if (props.loading) return
|
||
emit('cancel')
|
||
}
|
||
|
||
/** ESC 关闭;打开时锁背景滚动,关闭时复位 */
|
||
function onKeydown(e: KeyboardEvent) {
|
||
if (e.key === 'Escape' && props.open) onCancel()
|
||
}
|
||
|
||
watch(() => props.open, (isOpen) => {
|
||
if (!import.meta.client) return
|
||
document.body.style.overflow = isOpen ? 'hidden' : ''
|
||
})
|
||
|
||
onMounted(() => {
|
||
window.addEventListener('keydown', onKeydown)
|
||
})
|
||
|
||
onBeforeUnmount(() => {
|
||
window.removeEventListener('keydown', onKeydown)
|
||
if (import.meta.client) document.body.style.overflow = ''
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.hjc-confirm-fade-enter-active,
|
||
.hjc-confirm-fade-leave-active {
|
||
transition: opacity 0.2s ease;
|
||
}
|
||
.hjc-confirm-fade-enter-from,
|
||
.hjc-confirm-fade-leave-to {
|
||
opacity: 0;
|
||
}
|
||
|
||
.hjc-confirm-pop-enter-active {
|
||
transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.25s ease;
|
||
}
|
||
.hjc-confirm-pop-leave-active {
|
||
transition: transform 0.2s ease, opacity 0.2s ease;
|
||
}
|
||
.hjc-confirm-pop-enter-from {
|
||
transform: scale(0.95) translateY(10px);
|
||
opacity: 0;
|
||
}
|
||
.hjc-confirm-pop-leave-to {
|
||
transform: scale(0.95);
|
||
opacity: 0;
|
||
}
|
||
</style>
|