Files
danyang-match/h5-demo/src/pages/activity/ActivityMatchesPage.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

141 lines
3.7 KiB
Vue

<template>
<PageLayout title="活动缘分">
<div class="am">
<div v-if="activityTitle" class="am__activity">{{ activityTitle }}</div>
<UiErrorState v-if="error" @retry="load" />
<UiLoading v-else-if="loading" />
<template v-else>
<div v-if="matches.length">
<div
v-for="m in matches"
:key="m.targetUserId"
class="am__card"
@click="goResult(m)"
>
<UiAvatar :src="avatarOf(m)" :name="m.name" :size="52" />
<div class="am__info">
<div class="am__name">{{ m.name || '新朋友' }}</div>
<div class="am__meta">
<span v-if="m.age">{{ m.age }}岁</span>
<span v-if="m.locationName">{{ m.locationName }}</span>
</div>
</div>
<div class="am__cta">
<span class="am__cta-text">查看缘分</span>
<van-icon name="arrow" color="#9aa0b5" />
</div>
</div>
</div>
<UiEmpty v-else description="暂无双向匹配">
<van-button type="primary" round size="small" @click="router.back()">返回</van-button>
</UiEmpty>
</template>
</div>
</PageLayout>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { activityService } 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 activityId = computed(() => String(route.query.id || ''))
const matches = ref<any[]>([])
const activityTitle = ref('')
const loading = ref(true)
const error = ref(false)
function avatarOf(m: any): string {
const raw = m.avatarUrl || m.fileKey
return typeof raw === 'string' && raw.includes('://') ? raw : ''
}
async function load() {
loading.value = true
error.value = false
try {
if (!activityId.value) {
matches.value = []
return
}
const [result, detail]: any[] = await Promise.all([
activityService.getMutualMatches(activityId.value),
activityService.getActivityDetail(activityId.value).catch(() => null),
])
matches.value = result?.items || result?.records || (Array.isArray(result) ? result : [])
activityTitle.value = detail?.title || ''
} catch (err: any) {
error.value = true
appStore.toastError(err?.message || '加载活动缘分失败')
} finally {
loading.value = false
}
}
function goResult(m: any) {
if (!m?.targetUserId) return
router.push({
path: '/activity/mutual/result',
query: { id: activityId.value, partnerId: m.targetUserId },
})
}
onMounted(load)
</script>
<style scoped>
.am {
padding: 12px 16px 16px;
}
.am__activity {
font-size: 13px;
color: var(--mini-text-secondary);
margin-bottom: 10px;
}
.am__card {
display: flex;
align-items: center;
gap: 12px;
background: var(--mini-surface);
border-radius: var(--mini-radius);
padding: 12px;
margin-bottom: 10px;
box-shadow: var(--mini-shadow);
}
.am__info {
flex: 1;
min-width: 0;
}
.am__name {
font-size: 15px;
font-weight: 600;
color: var(--mini-text-strong);
}
.am__meta {
display: flex;
gap: 10px;
font-size: 12px;
color: var(--mini-text-secondary);
margin-top: 4px;
}
.am__cta {
display: flex;
align-items: center;
gap: 2px;
font-size: 12px;
color: var(--mini-tabbar-active);
flex: none;
}
</style>