Files
danyang-match/h5-demo/src/pages/activity/ActivityMutualWaitingPage.vue
T
WorkBuddy 69ebf33e18
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
release: freeze demo v1.1.0 2026-10-19
2026-10-19 20:36:22 +08:00

230 lines
6.3 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>
<PageLayout title="等待确认">
<div class="mw">
<UiErrorState v-if="error" @retry="load" />
<UiLoading v-else-if="loading" />
<UiEmpty v-else-if="!status.exists" description="未找到该活动的互选记录" />
<template v-else>
<div class="mw__partner">
<UiAvatar :src="partnerAvatar" :name="partnerName" :size="60" />
<div class="mw__partner-name">{{ partnerName }}</div>
</div>
<div v-if="bothConfirmed" class="mw__panel mw__panel--success">
<div class="mw__icon mw__icon--success">✓</div>
<div class="mw__title">双方已确认!</div>
<div class="mw__hint">
{{ hasActiveMatch ? '你们已有沟通入口,可前往缘分中心查看' : '沟通入口已开启,可前往缘分中心查看' }}
</div>
<van-button type="primary" round @click="goFate">
{{ hasActiveMatch ? '查看已有沟通' : '查看沟通' }}
</van-button>
</div>
<div v-else-if="partnerDeclined" class="mw__panel">
<div class="mw__icon">💤</div>
<div class="mw__title">对方暂不联系</div>
<div class="mw__hint">对方已选择暂不开通沟通,您可以稍后再看看其他缘分。</div>
<van-button round plain @click="router.back()">返回</van-button>
</div>
<div v-else class="mw__panel">
<div class="mw__icon">⏳</div>
<div class="mw__title">已发送邀请,等待对方确认</div>
<div class="mw__hint">对方确认后将收到平台通知;本页面会自动刷新状态</div>
<div class="mw__actions">
<van-button type="primary" round :loading="refreshing" @click="refresh">
刷新状态
</van-button>
<van-button round plain type="danger" :loading="declining" @click="onDecline">
暂不联系
</van-button>
</div>
</div>
</template>
</div>
</PageLayout>
</template>
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { activityMutualService } from '@/api/services'
import { useAppStore } from '@/stores/app'
import PageLayout from '@/layouts/PageLayout.vue'
import UiAvatar from '@/components/UiAvatar.vue'
import UiLoading from '@/components/UiLoading.vue'
import UiEmpty from '@/components/UiEmpty.vue'
import UiErrorState from '@/components/UiErrorState.vue'
const route = useRoute()
const router = useRouter()
const appStore = useAppStore()
const POLL_MS = 10000
const activityId = computed(() => String(route.query.id || ''))
const partnerId = computed(() => String(route.query.partnerId || ''))
const loading = ref(true)
const refreshing = ref(false)
const declining = ref(false)
const error = ref(false)
const status = ref<any>({ exists: false })
const partnerName = ref('新朋友')
const partnerAvatar = ref('')
const hasActiveMatch = ref(false)
let timer: ReturnType<typeof setInterval> | null = null
const bothConfirmed = computed(() => !!status.value?.bothConfirmed)
const partnerDeclined = computed(() => status.value?.partnerStatus === 'declined')
async function fetchStatus(isRefresh: boolean) {
if (isRefresh) refreshing.value = true
else loading.value = true
error.value = false
try {
if (!activityId.value || !partnerId.value) {
status.value = { exists: false }
return
}
const res: any = await activityMutualService.getMyConsentStatus(activityId.value, partnerId.value)
status.value = res || { exists: false }
// 对方确认后拉取一次结果,用于复用已有沟通(INV-019),并停止轮询
if (res?.bothConfirmed) {
await loadResult()
stopPoll()
}
} catch (err: any) {
error.value = true
if (isRefresh) appStore.toastError(err?.message || '刷新失败')
} finally {
loading.value = false
refreshing.value = false
}
}
async function loadResult() {
try {
const list: any = await activityMutualService.getResults(activityId.value)
const arr: any[] = list?.items || list?.records || (Array.isArray(list) ? list : [])
const me = arr.find((x) => (x.partnerId || x.targetUserId) === partnerId.value)
if (me) {
partnerName.value = me.partnerName || me.name || partnerName.value
hasActiveMatch.value = !!me.hasActiveMatch
}
} catch {
/* 结果信息可选,不影响等待状态 */
}
}
function startPoll() {
if (timer) return
timer = setInterval(() => {
if (bothConfirmed.value) return
fetchStatus(true)
}, POLL_MS)
}
function stopPoll() {
if (timer) {
clearInterval(timer)
timer = null
}
}
function load() {
fetchStatus(false)
}
function refresh() {
fetchStatus(true)
}
async function onDecline() {
if (declining.value) return
declining.value = true
try {
await activityMutualService.declineConnection(activityId.value, partnerId.value)
appStore.toastSuccess('已选择暂不联系')
router.back()
} catch (err: any) {
appStore.toastError(err?.message || '操作失败')
} finally {
declining.value = false
}
}
function goFate() {
router.push('/fate-center')
}
onMounted(() => {
load()
startPoll()
})
onUnmounted(stopPoll)
</script>
<style scoped>
.mw {
padding: 12px 16px 16px;
}
.mw__partner {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 28px 0 12px;
}
.mw__partner-name {
font-size: 17px;
font-weight: 600;
color: var(--mini-text-strong);
}
.mw__panel {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
background: var(--mini-surface);
border-radius: var(--mini-radius);
padding: 28px 16px;
box-shadow: var(--mini-shadow);
text-align: center;
}
.mw__panel--success {
background: rgba(47, 179, 109, 0.08);
}
.mw__icon {
font-size: 44px;
}
.mw__icon--success {
width: 52px;
height: 52px;
border-radius: 50%;
background: var(--mini-success);
color: #fff;
font-size: 26px;
display: flex;
align-items: center;
justify-content: center;
}
.mw__title {
font-size: 16px;
font-weight: 600;
color: var(--mini-text-strong);
}
.mw__hint {
font-size: 12px;
color: var(--mini-text-secondary);
line-height: 1.6;
}
.mw__actions {
display: flex;
gap: 12px;
margin-top: 6px;
}
</style>