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

232 lines
6.6 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="cc">
<div class="cc__intro">
<div class="cc__intro-title">红娘联系授权</div>
<div class="cc__intro-text">
红娘经平台审批后会向你发起联系授权申请。你可以单独决定同意或拒绝,同意后在有效期内生效,
到期自动失效,也可随时被撤销。
</div>
</div>
<UiLoading v-if="loading" text="加载中…" />
<UiErrorState v-else-if="error" @retry="load" />
<template v-else>
<!-- 待确认队列 -->
<template v-if="pending.length">
<div class="cc__section-title">待你确认({{ pending.length }})</div>
<div v-for="item in pending" :key="item.id" class="card">
<div class="cc__row">
<span class="cc__label">授权渠道</span>
<span class="cc__value cc__value--strong">{{ channelLabel(item.channel) }}</span>
</div>
<div class="cc__row">
<span class="cc__label">授权目的</span>
<span class="cc__value">{{ item.purpose || '—' }}</span>
</div>
<div class="cc__row">
<span class="cc__label">确认截止</span>
<span class="cc__value">{{ formatDateTime(item.expiresAt) || '—' }}</span>
</div>
<div class="cc__actions">
<van-button size="small" plain :loading="actingId === item.id" @click="onDecline(item)">
拒绝
</van-button>
<van-button size="small" type="primary" :loading="actingId === item.id" @click="onConsent(item)">
同意
</van-button>
</div>
</div>
</template>
<UiEmpty v-else description="暂无待确认的授权申请" />
<!-- 历史记录 -->
<template v-if="list.length">
<div class="cc__section-title">历史记录</div>
<div v-for="item in list" :key="item.id" class="card card--slim">
<div class="cc__row">
<span class="cc__label">渠道</span>
<span class="cc__value">{{ channelLabel(item.channel) }}</span>
</div>
<div class="cc__row">
<span class="cc__label">目的</span>
<span class="cc__value">{{ item.purpose || '—' }}</span>
</div>
<div class="cc__row">
<span class="cc__label">状态</span>
<span class="cc__value">
<UiTag :type="statusTag(item.status)">{{ statusLabel(item.status) }}</UiTag>
</span>
</div>
</div>
</template>
</template>
</div>
</PageLayout>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { showConfirmDialog } from 'vant'
import { contactConsentService } from '@/api/services'
import { useAppStore } from '@/stores/app'
import { formatDateTime } from '@/utils/datetime'
import PageLayout from '@/layouts/PageLayout.vue'
import UiLoading from '@/components/UiLoading.vue'
import UiEmpty from '@/components/UiEmpty.vue'
import UiErrorState from '@/components/UiErrorState.vue'
import UiTag from '@/components/UiTag.vue'
const appStore = useAppStore()
const loading = ref(true)
const error = ref(false)
const pending = ref<any[]>([])
const list = ref<any[]>([])
const actingId = ref('')
const CHANNEL_LABELS: Record<string, string> = {
phone: '电话联系',
offline_meeting: '线下见面',
full_contact: '完整联系方式',
}
const STATUS_LABELS: Record<string, string> = {
pending_approval: '待审批',
pending_consent: '待你确认',
active: '生效中',
rejected: '已拒绝',
expired: '已过期',
revoked: '已撤销',
}
function channelLabel(code: string): string {
return CHANNEL_LABELS[code] || code || '—'
}
function statusLabel(code: string): string {
return STATUS_LABELS[code] || code || '—'
}
function statusTag(code: string): 'primary' | 'success' | 'warning' | 'danger' | 'info' {
if (code === 'active') return 'success'
if (code === 'pending_consent' || code === 'pending_approval') return 'warning'
if (code === 'rejected' || code === 'revoked') return 'danger'
return 'info'
}
async function load() {
loading.value = true
error.value = false
try {
const result: any = await contactConsentService.my()
pending.value = result?.pendingConsent || []
list.value = result?.list || result?.items || result?.records || []
} catch {
error.value = true
} finally {
loading.value = false
}
}
async function onConsent(item: any) {
try {
await showConfirmDialog({
title: '确认同意联系授权',
message: `同意「${channelLabel(item.channel)}」:${item.purpose || ''}。同意后红娘可在有效期内通过该方式联系你。`,
})
} catch {
return
}
actingId.value = item.id
try {
await contactConsentService.consent(item.id)
appStore.toastSuccess('已同意')
await load()
} catch (err: any) {
appStore.toastError(err?.message || '操作失败')
} finally {
actingId.value = ''
}
}
async function onDecline(item: any) {
actingId.value = item.id
try {
await contactConsentService.decline(item.id)
appStore.toast('已拒绝')
await load()
} catch (err: any) {
appStore.toastError(err?.message || '操作失败')
} finally {
actingId.value = ''
}
}
onMounted(load)
</script>
<style scoped>
.cc {
padding: 16px;
}
.cc__intro {
background: rgba(255, 79, 145, 0.06);
border: 1px solid rgba(255, 79, 145, 0.16);
border-radius: var(--mini-radius);
padding: 14px;
margin-bottom: 12px;
}
.cc__intro-title {
font-size: 15px;
font-weight: 600;
color: var(--mini-text-strong);
}
.cc__intro-text {
font-size: 12px;
color: var(--mini-text-secondary);
line-height: 1.7;
margin-top: 6px;
}
.cc__section-title {
font-size: 14px;
font-weight: 600;
color: var(--mini-text-strong);
margin: 16px 0 8px;
}
.card {
background: var(--mini-surface);
border-radius: var(--mini-radius);
box-shadow: var(--mini-shadow);
padding: 14px;
margin-bottom: 10px;
}
.card--slim {
padding: 12px 14px;
}
.cc__row {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 4px 0;
}
.cc__label {
font-size: 13px;
color: var(--mini-text-secondary);
flex: none;
}
.cc__value {
font-size: 13px;
color: var(--mini-text);
text-align: right;
}
.cc__value--strong {
font-weight: 600;
color: var(--mini-text-strong);
}
.cc__actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 12px;
}
</style>