Files
hjc-web/app/components/HjcConfirmDialog.vue
T
weicw1996 a7399e0624 feat(tender): 我的订单加「去支付」「取消订单」入口,并新建收银台
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 字符串(包装方式一改就会静默失效)
2026-09-17 03:39:59 +08:00

129 lines
3.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>