1330 lines
32 KiB
Vue
1330 lines
32 KiB
Vue
<template>
|
||
<view class="profile-detail-page">
|
||
<!-- 加载状态 -->
|
||
<Loading type="page" :loading="loading" />
|
||
|
||
<!-- 资料完善提示弹窗 -->
|
||
<Popup
|
||
:visible="showProfilePopup"
|
||
type="profile"
|
||
@close="showProfilePopup = false"
|
||
@goProfile="handleGoProfile"
|
||
/>
|
||
|
||
<!-- 解锁确认弹窗 -->
|
||
<Popup
|
||
:visible="showUnlockPopup"
|
||
type="unlock"
|
||
:remainingQuota="remainingUnlockQuota"
|
||
@close="showUnlockPopup = false"
|
||
@unlock="handleConfirmUnlock"
|
||
@goMember="handleGoMember"
|
||
/>
|
||
|
||
<!-- 拨打电话底部弹窗 -->
|
||
<view class="phone-popup-mask" v-if="showPhonePopup" @click="showPhonePopup = false">
|
||
<view class="phone-popup" @click.stop>
|
||
<view class="phone-popup-header">
|
||
<text class="phone-popup-title">{{ userDetail?.nickname }}的联系电话</text>
|
||
<text class="phone-popup-close" @click="showPhonePopup = false">×</text>
|
||
</view>
|
||
<view class="phone-popup-content">
|
||
<text class="phone-number">{{ userDetail?.phone || '暂无电话' }}</text>
|
||
</view>
|
||
<button class="phone-popup-btn" @click="handleMakeCall" :disabled="!userDetail?.phone">
|
||
拨打电话
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 自定义导航栏 -->
|
||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<view class="navbar-content">
|
||
<view class="navbar-back" @click="handleBack">
|
||
<text class="back-icon">‹</text>
|
||
</view>
|
||
<text class="navbar-title">详情资料</text>
|
||
<view class="navbar-placeholder"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 内容区域 -->
|
||
<scroll-view
|
||
class="content-scroll"
|
||
scroll-y
|
||
:style="{ paddingTop: (statusBarHeight + 44) + 'px' }"
|
||
>
|
||
<view class="content-wrapper" v-if="userDetail">
|
||
<!-- 用户头像和基本信息 -->
|
||
<view class="user-header-card">
|
||
<view class="header-left">
|
||
<image
|
||
class="avatar"
|
||
:src="userDetail.avatar"
|
||
mode="aspectFill"
|
||
/>
|
||
</view>
|
||
<view class="header-right">
|
||
<view class="name-row">
|
||
<text class="nickname">{{ userDetail.nickname }}</text>
|
||
<text class="relationship" v-if="!userDetail.nickname?.includes('(')">({{ relationshipText }})</text>
|
||
<view class="header-tags">
|
||
<image v-if="userDetail.isMember && memberIconUrl" class="member-icon" :src="memberIconUrl" mode="aspectFit" />
|
||
<text v-else-if="userDetail.isMember" class="tag tag-member">会员</text>
|
||
<text v-if="userDetail.isRealName" class="tag tag-realname">已实名</text>
|
||
</view>
|
||
</view>
|
||
<text class="xiangqin-no">相亲ID: {{ userDetail.xiangQinNo }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 孩子的基本资料 -->
|
||
<view class="section-card">
|
||
<view class="section-title">孩子的基本资料</view>
|
||
<view class="gender-year-row">
|
||
<text class="gender-year" :class="{ male: userDetail.childGender === 1 }">
|
||
{{ genderText }} · {{ userDetail.birthYear }}年
|
||
</text>
|
||
<view class="flex-spacer"></view>
|
||
<button class="share-btn" open-type="share">分享</button>
|
||
</view>
|
||
|
||
<view class="info-grid">
|
||
<view class="info-item">
|
||
<text class="label">年龄</text>
|
||
<text class="value">{{ userDetail.age }}岁</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">身高</text>
|
||
<text class="value">{{ userDetail.height }}cm</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">学历</text>
|
||
<text class="value">{{ educationText }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">体重</text>
|
||
<text class="value">{{ userDetail.weight ? userDetail.weight + 'kg' : '未填写' }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">收入</text>
|
||
<text class="value">{{ incomeText }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">职业</text>
|
||
<text class="value">{{ userDetail.occupation || '未填写' }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">现居</text>
|
||
<text class="value">{{ userDetail.workCity || '未填写' }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">家乡</text>
|
||
<text class="value">{{ userDetail.homeCity || '未填写' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 孩子的照片 -->
|
||
<view class="section-card" v-if="displayPhotos.length > 0">
|
||
<view class="section-title-row">
|
||
<text class="section-title">孩子的照片</text>
|
||
<text class="photo-count">共{{ displayPhotos.length }}张</text>
|
||
</view>
|
||
<view class="photo-grid">
|
||
<view
|
||
v-for="(photo, index) in displayPhotos"
|
||
:key="index"
|
||
class="photo-item"
|
||
@click="previewPhoto(index)"
|
||
>
|
||
<image
|
||
class="photo-image"
|
||
:class="{ 'photo-blurred': !userDetail.isPhotoPublic && !isUnlocked }"
|
||
:src="photo.photoUrl"
|
||
mode="aspectFill"
|
||
/>
|
||
<view v-if="!userDetail.isPhotoPublic && !isUnlocked" class="photo-lock-overlay"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 孩子的相亲说明 -->
|
||
<view class="section-card" v-if="userDetail.introduction">
|
||
<view class="section-title">孩子的相亲说明</view>
|
||
<view class="intro-content">
|
||
<text>{{ userDetail.introduction }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 孩子的择偶标准 -->
|
||
<view class="section-card">
|
||
<view class="section-title">孩子的择偶标准</view>
|
||
<view class="requirement-grid">
|
||
<view class="req-item">
|
||
<text class="label">年龄范围</text>
|
||
<text class="value">{{ requirementAgeText }}</text>
|
||
</view>
|
||
<view class="req-item">
|
||
<text class="label">身高要求</text>
|
||
<text class="value">{{ requirementHeightText }}</text>
|
||
</view>
|
||
<view class="req-item">
|
||
<text class="label">学历要求</text>
|
||
<text class="value">{{ requirementEducationText }}</text>
|
||
</view>
|
||
<view class="req-item">
|
||
<text class="label">城市要求</text>
|
||
<text class="value">{{ requirementCityText }}</text>
|
||
</view>
|
||
<view class="req-item">
|
||
<text class="label">收入要求</text>
|
||
<text class="value">{{ requirementIncomeText }}</text>
|
||
</view>
|
||
<view class="req-item">
|
||
<text class="label">房产要求</text>
|
||
<text class="value">{{ requirementHouseText }}</text>
|
||
</view>
|
||
<view class="req-item">
|
||
<text class="label">车产要求</text>
|
||
<text class="value">{{ requirementCarText }}</text>
|
||
</view>
|
||
<view class="req-item">
|
||
<text class="label">婚姻要求</text>
|
||
<text class="value">{{ requirementMarriageText }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 孩子的更多信息 -->
|
||
<view class="section-card">
|
||
<view class="section-title">孩子的更多信息</view>
|
||
<view class="more-info-grid">
|
||
<view class="more-item">
|
||
<text class="label">父母情况</text>
|
||
<text class="value">{{ userDetail.parentStatus || '未填写' }}</text>
|
||
</view>
|
||
<view class="more-item">
|
||
<text class="label">现居地有房</text>
|
||
<text class="value">{{ houseText }}</text>
|
||
</view>
|
||
<view class="more-item">
|
||
<text class="label">婚姻状况</text>
|
||
<text class="value">{{ marriageText }}</text>
|
||
</view>
|
||
<view class="more-item">
|
||
<text class="label">生肖属相</text>
|
||
<text class="value">{{ zodiacText }}</text>
|
||
</view>
|
||
<view class="more-item full-width">
|
||
<text class="label">何时结婚</text>
|
||
<text class="value">{{ expectMarryText }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 孩子的基本资料(父母信息) -->
|
||
<view class="section-card">
|
||
<view class="section-title">父母的基本资料</view>
|
||
<view class="parent-info-grid">
|
||
<view class="parent-item">
|
||
<text class="label">父母情况</text>
|
||
<text class="value">{{ userDetail.parentStatus || '未填写' }}</text>
|
||
</view>
|
||
<view class="parent-item">
|
||
<text class="label">退休情况</text>
|
||
<text class="value">{{ userDetail.parentRetireStatus || '未填写' }}</text>
|
||
</view>
|
||
<view class="parent-item">
|
||
<text class="label">父母现居</text>
|
||
<text class="value">{{ userDetail.parentCity || '未填写' }}</text>
|
||
</view>
|
||
<view class="parent-item">
|
||
<text class="label">住房情况</text>
|
||
<text class="value">{{ userDetail.parentHousingStatus || '未填写' }}</text>
|
||
</view>
|
||
<view class="parent-item">
|
||
<text class="label">养老保险</text>
|
||
<text class="value">{{ userDetail.parentPensionStatus || '未填写' }}</text>
|
||
</view>
|
||
<view class="parent-item">
|
||
<text class="label">医疗保险</text>
|
||
<text class="value">{{ userDetail.parentMedicalStatus || '未填写' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 联系方式(已解锁时显示) -->
|
||
<view class="section-card" v-if="isUnlocked && userDetail.weChatNo">
|
||
<view class="section-title">联系方式</view>
|
||
<view class="contact-row">
|
||
<text class="label">微信号</text>
|
||
<text class="value">{{ userDetail.weChatNo }}</text>
|
||
<button class="copy-btn" @click="handleCopyWeChat">复制</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部提示 -->
|
||
<view class="bottom-tip" v-if="!isUnlocked">
|
||
<text>感兴趣请尽早联系~</text>
|
||
<text>明天将无法查看对方资料</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部操作栏 -->
|
||
<view class="bottom-actions" v-if="userDetail">
|
||
<button class="btn-greet" @click="handleContact">打招呼</button>
|
||
<button
|
||
class="btn-favorite"
|
||
:class="{ active: isFavorited }"
|
||
@click="handleFavorite"
|
||
>
|
||
{{ isFavorited ? '已收藏' : '收藏' }}
|
||
</button>
|
||
<button class="btn-call" @click="handleCall">拨打电话</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { onShareAppMessage } from '@dcloudio/uni-app'
|
||
import { useUserStore } from '@/store/user.js'
|
||
import { useConfigStore } from '@/store/config.js'
|
||
import { getUserDetail } from '@/api/user.js'
|
||
import { favorite, unlock } from '@/api/interact.js'
|
||
import { getFullImageUrl } from '@/utils/image.js'
|
||
import Loading from '@/components/Loading/index.vue'
|
||
import Popup from '@/components/Popup/index.vue'
|
||
|
||
const userStore = useUserStore()
|
||
const configStore = useConfigStore()
|
||
|
||
// 状态栏高度
|
||
const statusBarHeight = ref(20)
|
||
|
||
// 页面参数
|
||
const userId = ref(0)
|
||
|
||
// 状态
|
||
const loading = ref(true)
|
||
const userDetail = ref(null)
|
||
const isFavorited = ref(false)
|
||
const isUnlocked = ref(false)
|
||
const remainingUnlockQuota = ref(0)
|
||
const showProfilePopup = ref(false)
|
||
const showUnlockPopup = ref(false)
|
||
const showPhonePopup = ref(false)
|
||
|
||
// 获取系统信息
|
||
const getSystemInfo = () => {
|
||
uni.getSystemInfo({
|
||
success: (res) => {
|
||
statusBarHeight.value = res.statusBarHeight || 20
|
||
}
|
||
})
|
||
}
|
||
|
||
// 返回
|
||
const handleBack = () => {
|
||
const pages = getCurrentPages()
|
||
if (pages.length > 1) {
|
||
// 有上一页,正常返回
|
||
uni.navigateBack()
|
||
} else {
|
||
// 没有上一页(从分享链接进入),跳转到首页
|
||
uni.switchTab({
|
||
url: '/pages/index/index'
|
||
})
|
||
}
|
||
}
|
||
|
||
// 选项映射
|
||
const educationMap = {
|
||
1: '高中',
|
||
2: '中专',
|
||
3: '大专',
|
||
4: '本科',
|
||
5: '研究生',
|
||
6: '博士及以上'
|
||
}
|
||
|
||
const incomeMap = {
|
||
1: '5千以下',
|
||
2: '5千-8千/月',
|
||
3: '8千-1万/月',
|
||
4: '1万-2万/月',
|
||
5: '2万以上'
|
||
}
|
||
|
||
const houseMap = {
|
||
1: '现居地已购房',
|
||
2: '家乡已购房',
|
||
3: '婚后购房',
|
||
4: '父母同住',
|
||
5: '租房',
|
||
6: '近期有购房计划'
|
||
}
|
||
|
||
const carMap = {
|
||
1: '已购车',
|
||
2: '无车',
|
||
3: '近期购车'
|
||
}
|
||
|
||
const marriageMap = {
|
||
1: '未婚',
|
||
2: '离异未育',
|
||
3: '离异已育'
|
||
}
|
||
|
||
const expectMarryMap = {
|
||
1: '尽快结婚',
|
||
2: '一到两年内',
|
||
3: '孩子满意就结婚'
|
||
}
|
||
|
||
const relationshipMap = {
|
||
1: '父亲',
|
||
2: '母亲',
|
||
3: '本人'
|
||
}
|
||
|
||
// 生肖计算
|
||
const zodiacList = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']
|
||
const getZodiac = (year) => {
|
||
if (!year) return '未知'
|
||
return zodiacList[(year - 4) % 12]
|
||
}
|
||
|
||
// 计算属性
|
||
const displayPhotos = computed(() => {
|
||
if (!userDetail.value || !userDetail.value.photos) return []
|
||
return userDetail.value.photos.map(p => ({
|
||
...p,
|
||
photoUrl: getFullImageUrl(p.photoUrl)
|
||
}))
|
||
})
|
||
|
||
const genderText = computed(() => {
|
||
return userDetail.value?.childGender === 1 ? '男' : '女'
|
||
})
|
||
|
||
const relationshipText = computed(() => {
|
||
return relationshipMap[userDetail.value?.relationship] || '本人'
|
||
})
|
||
|
||
const memberIconUrl = computed(() => {
|
||
if (!userDetail.value?.isMember || !userDetail.value?.memberLevel) return ''
|
||
const iconUrl = configStore.getMemberIcon(userDetail.value.memberLevel)
|
||
return iconUrl ? getFullImageUrl(iconUrl) : ''
|
||
})
|
||
|
||
const educationText = computed(() => {
|
||
return educationMap[userDetail.value?.education] || '未填写'
|
||
})
|
||
|
||
const incomeText = computed(() => {
|
||
return incomeMap[userDetail.value?.monthlyIncome] || '未填写'
|
||
})
|
||
|
||
const houseText = computed(() => {
|
||
return houseMap[userDetail.value?.houseStatus] || '未填写'
|
||
})
|
||
|
||
const carText = computed(() => {
|
||
return carMap[userDetail.value?.carStatus] || '未填写'
|
||
})
|
||
|
||
const marriageText = computed(() => {
|
||
return marriageMap[userDetail.value?.marriageStatus] || '未填写'
|
||
})
|
||
|
||
const expectMarryText = computed(() => {
|
||
return expectMarryMap[userDetail.value?.expectMarryTime] || '未填写'
|
||
})
|
||
|
||
const zodiacText = computed(() => {
|
||
return getZodiac(userDetail.value?.birthYear)
|
||
})
|
||
|
||
// 择偶要求文本 - 有范围的显示"不限",其他显示"无"
|
||
const requirementAgeText = computed(() => {
|
||
const req = userDetail.value?.requirement
|
||
if (!req) return '不限'
|
||
if (req.ageMin && req.ageMax) return `${req.ageMin}-${req.ageMax}岁`
|
||
if (req.ageMin) return `${req.ageMin}岁以上`
|
||
if (req.ageMax) return `${req.ageMax}岁以下`
|
||
return '不限'
|
||
})
|
||
|
||
const requirementHeightText = computed(() => {
|
||
const req = userDetail.value?.requirement
|
||
if (!req) return '不限'
|
||
if (req.heightMin && req.heightMax) return `${req.heightMin}cm-${req.heightMax}cm`
|
||
if (req.heightMin) return `${req.heightMin}cm以上`
|
||
if (req.heightMax) return `${req.heightMax}cm以下`
|
||
return '不限'
|
||
})
|
||
|
||
const requirementEducationText = computed(() => {
|
||
const req = userDetail.value?.requirement
|
||
if (!req || !req.education || req.education.length === 0) return '无'
|
||
return req.education.map(e => educationMap[e]).filter(Boolean).join('、')
|
||
})
|
||
|
||
const requirementCityText = computed(() => {
|
||
const req = userDetail.value?.requirement
|
||
if (!req) return '无'
|
||
const cities = []
|
||
if (req.city1City) cities.push(req.city1City)
|
||
if (req.city2City) cities.push(req.city2City)
|
||
return cities.length > 0 ? cities.join('、') : '无'
|
||
})
|
||
|
||
const requirementIncomeText = computed(() => {
|
||
const req = userDetail.value?.requirement
|
||
if (!req || !req.monthlyIncomeMin) return '不限'
|
||
return incomeMap[req.monthlyIncomeMin] || '不限'
|
||
})
|
||
|
||
// 房产要求文本
|
||
const requirementHouseText = computed(() => {
|
||
const req = userDetail.value?.requirement
|
||
if (!req || !req.houseStatus || req.houseStatus.length === 0) return '无'
|
||
return req.houseStatus.map(h => houseMap[h]).filter(Boolean).join('、')
|
||
})
|
||
|
||
// 车产要求文本
|
||
const requirementCarText = computed(() => {
|
||
const req = userDetail.value?.requirement
|
||
if (!req || !req.carStatus || req.carStatus.length === 0) return '无'
|
||
return req.carStatus.map(c => carMap[c]).filter(Boolean).join('、')
|
||
})
|
||
|
||
// 婚姻要求文本
|
||
const requirementMarriageText = computed(() => {
|
||
const req = userDetail.value?.requirement
|
||
if (!req || !req.marriageStatus || req.marriageStatus.length === 0) return '无'
|
||
return req.marriageStatus.map(m => marriageMap[m]).filter(Boolean).join('、')
|
||
})
|
||
|
||
// 加载用户详情
|
||
const loadUserDetail = async () => {
|
||
loading.value = true
|
||
try {
|
||
const res = await getUserDetail(userId.value)
|
||
// 后端返回 code: 0 表示成功
|
||
if (res && (res.success || res.code === 0) && res.data) {
|
||
userDetail.value = res.data
|
||
isFavorited.value = res.data.isFavorited || false
|
||
isUnlocked.value = res.data.isUnlocked || false
|
||
remainingUnlockQuota.value = res.data.remainingUnlockQuota || 0
|
||
}
|
||
} catch (error) {
|
||
console.error('加载用户详情失败:', error)
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 预览照片
|
||
const previewPhoto = (index) => {
|
||
if (!userDetail.value?.isPhotoPublic && !isUnlocked.value) {
|
||
uni.showToast({ title: '私密照片,解锁后可查看', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
const urls = displayPhotos.value.map(p => p.photoUrl)
|
||
uni.previewImage({
|
||
urls,
|
||
current: urls[index] || urls[0]
|
||
})
|
||
}
|
||
|
||
// 收藏处理
|
||
const handleFavorite = async () => {
|
||
// 检查是否登录
|
||
if (!userStore.isLoggedIn) {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '请先登录后再收藏',
|
||
confirmText: '去登录',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.navigateTo({ url: '/pages/login/index' })
|
||
}
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
try {
|
||
const res = await favorite(userId.value)
|
||
if (res && (res.success || res.code === 0)) {
|
||
isFavorited.value = res.data?.isFavorited ?? !isFavorited.value
|
||
uni.showToast({
|
||
title: isFavorited.value ? '收藏成功' : '已取消收藏',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
} catch (error) {
|
||
uni.showToast({ title: '操作失败', icon: 'none' })
|
||
}
|
||
}
|
||
|
||
// 联系处理
|
||
const handleContact = async () => {
|
||
console.log('[Detail] handleContact - 检查实名状态:', {
|
||
targetIsRealName: userDetail.value?.isRealName,
|
||
myIsRealName: userStore.isRealName,
|
||
isLoggedIn: userStore.isLoggedIn,
|
||
isProfileCompleted: userStore.isProfileCompleted,
|
||
isProfileAuditing: userStore.isProfileAuditing
|
||
})
|
||
|
||
// 检查是否登录
|
||
if (!userStore.isLoggedIn) {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '请先登录后再联系对方',
|
||
confirmText: '去登录',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.navigateTo({ url: '/pages/login/index' })
|
||
}
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
// 检查是否资料审核中
|
||
if (userStore.isProfileAuditing) {
|
||
uni.showModal({
|
||
title: '资料审核中',
|
||
content: '正在加急审核您的相亲资料,请耐心等待',
|
||
showCancel: false,
|
||
confirmText: '确定'
|
||
})
|
||
return
|
||
}
|
||
|
||
if (!userStore.isProfileCompleted) {
|
||
showProfilePopup.value = true
|
||
return
|
||
}
|
||
|
||
// 如果对方已实名,检查当前用户是否也已实名
|
||
if (userDetail.value?.isRealName && !userStore.isRealName) {
|
||
console.log('[Detail] 对方已实名但我未实名,跳转实名认证页')
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '对方已开启实名相亲,请先完成实名认证才能联系',
|
||
confirmText: '去认证',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.navigateTo({ url: '/pages/realname/index' })
|
||
}
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
if (isUnlocked.value) {
|
||
// 已解锁,直接跳转聊天页
|
||
uni.navigateTo({ url: `/pages/chat/index?targetUserId=${userId.value}` })
|
||
} else if (userStore.isMember) {
|
||
// 会员直接解锁,不显示弹窗
|
||
try {
|
||
const res = await unlock(userId.value)
|
||
if (res && (res.success || res.code === 0)) {
|
||
isUnlocked.value = true
|
||
uni.navigateTo({ url: `/pages/chat/index?targetUserId=${userId.value}` })
|
||
}
|
||
} catch (error) {
|
||
uni.showToast({ title: '解锁失败', icon: 'none' })
|
||
}
|
||
} else {
|
||
// 非会员显示解锁弹窗
|
||
showUnlockPopup.value = true
|
||
}
|
||
}
|
||
|
||
// 去完善资料
|
||
const handleGoProfile = () => {
|
||
showProfilePopup.value = false
|
||
uni.navigateTo({ url: '/pages/profile/edit' })
|
||
}
|
||
|
||
// 确认解锁
|
||
const handleConfirmUnlock = async () => {
|
||
if (remainingUnlockQuota.value <= 0) {
|
||
showUnlockPopup.value = false
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '您的解锁次数已用完,请购买会员获取更多解锁次数',
|
||
confirmText: '去购买',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.navigateTo({ url: '/pages/member/index' })
|
||
}
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
try {
|
||
const res = await unlock(userId.value)
|
||
if (res && (res.success || res.code === 0)) {
|
||
isUnlocked.value = true
|
||
showUnlockPopup.value = false
|
||
remainingUnlockQuota.value = Math.max(0, remainingUnlockQuota.value - 1)
|
||
uni.navigateTo({ url: `/pages/chat/index?targetUserId=${userId.value}` })
|
||
}
|
||
} catch (error) {
|
||
uni.showToast({ title: '解锁失败', icon: 'none' })
|
||
}
|
||
}
|
||
|
||
// 跳转会员页面
|
||
const handleGoMember = () => {
|
||
showUnlockPopup.value = false
|
||
uni.navigateTo({ url: '/pages/member/index' })
|
||
}
|
||
|
||
// 拨打电话
|
||
const handleCall = () => {
|
||
// 检查是否登录
|
||
if (!userStore.isLoggedIn) {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '请先登录后再联系对方',
|
||
confirmText: '去登录',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.navigateTo({ url: '/pages/login/index' })
|
||
}
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
if (!isUnlocked.value) {
|
||
showUnlockPopup.value = true
|
||
return
|
||
}
|
||
|
||
// 已解锁,显示电话弹窗
|
||
showPhonePopup.value = true
|
||
}
|
||
|
||
// 拨打电话
|
||
const handleMakeCall = () => {
|
||
const phone = userDetail.value?.phone
|
||
if (!phone) {
|
||
uni.showToast({ title: '暂无电话号码', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
uni.makePhoneCall({
|
||
phoneNumber: phone,
|
||
fail: (err) => {
|
||
console.error('拨打电话失败:', err)
|
||
}
|
||
})
|
||
}
|
||
|
||
// 复制微信号
|
||
const handleCopyWeChat = () => {
|
||
if (userDetail.value?.weChatNo) {
|
||
uni.setClipboardData({
|
||
data: userDetail.value.weChatNo,
|
||
success: () => {
|
||
uni.showToast({ title: '已复制微信号', icon: 'success' })
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// 页面加载
|
||
onMounted(() => {
|
||
getSystemInfo()
|
||
|
||
const pages = getCurrentPages()
|
||
const currentPage = pages[pages.length - 1]
|
||
const options = currentPage?.options || {}
|
||
|
||
if (options.userId) {
|
||
userId.value = parseInt(options.userId)
|
||
loadUserDetail()
|
||
} else {
|
||
uni.showToast({ title: '参数错误', icon: 'none' })
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
}
|
||
})
|
||
|
||
// 分享配置
|
||
onShareAppMessage(() => {
|
||
return {
|
||
title: userDetail.value?.nickname ? `${userDetail.value.nickname}的相亲资料` : '相宜相亲',
|
||
path: `/pages/profile/detail?userId=${userId.value}`,
|
||
imageUrl: '/static/logo.png'
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.profile-detail-page {
|
||
min-height: 100vh;
|
||
background-color: #f5f6fa;
|
||
overflow: hidden;
|
||
}
|
||
|
||
// 自定义导航栏
|
||
.custom-navbar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
background: linear-gradient(135deg, #ffb5b5 0%, #ff9a9a 100%);
|
||
|
||
.navbar-content {
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 24rpx;
|
||
|
||
.navbar-back {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.back-icon {
|
||
font-size: 56rpx;
|
||
color: #fff;
|
||
font-weight: 400;
|
||
}
|
||
}
|
||
|
||
.navbar-title {
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
}
|
||
|
||
.navbar-placeholder {
|
||
width: 60rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 内容滚动区域
|
||
.content-scroll {
|
||
height: calc(100vh - 160rpx);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.content-wrapper {
|
||
padding: 24rpx;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
// 用户头像卡片
|
||
.user-header-card {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 24rpx;
|
||
background: #fff;
|
||
border-radius: 24rpx;
|
||
margin-bottom: 24rpx;
|
||
|
||
.header-left {
|
||
.avatar {
|
||
width: 100rpx;
|
||
height: 100rpx;
|
||
border-radius: 50%;
|
||
background: #f0f0f0;
|
||
}
|
||
}
|
||
|
||
.header-right {
|
||
margin-left: 24rpx;
|
||
flex: 1;
|
||
|
||
.name-row {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 8rpx;
|
||
flex-wrap: wrap;
|
||
gap: 8rpx;
|
||
|
||
.nickname {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.relationship {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.header-tags {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
margin-left: 8rpx;
|
||
vertical-align: middle;
|
||
|
||
.member-icon {
|
||
height: 42rpx;
|
||
width: 120rpx;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.tag {
|
||
display: inline-block;
|
||
font-size: 22rpx;
|
||
padding: 6rpx 16rpx;
|
||
border-radius: 8rpx;
|
||
height: 36rpx;
|
||
line-height: 24rpx;
|
||
box-sizing: border-box;
|
||
vertical-align: middle;
|
||
|
||
&.tag-realname {
|
||
background: #3d4a4a;
|
||
color: #fff;
|
||
}
|
||
|
||
&.tag-member {
|
||
background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%);
|
||
color: #ff9800;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.xiangqin-no {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 通用卡片样式
|
||
.section-card {
|
||
background: #fff;
|
||
border-radius: 24rpx;
|
||
padding: 28rpx;
|
||
margin-bottom: 24rpx;
|
||
|
||
.section-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.section-title-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 24rpx;
|
||
|
||
.section-title {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.photo-count {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 性别年份行
|
||
.gender-year-row {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 24rpx;
|
||
|
||
.gender-year {
|
||
font-size: 40rpx;
|
||
font-weight: 600;
|
||
color: #ff6b6b;
|
||
|
||
&.male {
|
||
color: #4a90d9;
|
||
}
|
||
}
|
||
|
||
.flex-spacer {
|
||
flex: 1;
|
||
}
|
||
|
||
.share-btn {
|
||
padding: 16rpx 40rpx;
|
||
font-size: 28rpx;
|
||
color: #fff;
|
||
background: #4cd964;
|
||
border: none;
|
||
border-radius: 12rpx;
|
||
line-height: 1;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 信息网格
|
||
.info-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
|
||
.info-item {
|
||
width: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
width: 80rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-left: 16rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 照片网格
|
||
.photo-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16rpx;
|
||
|
||
.photo-item {
|
||
width: calc(33.33% - 12rpx);
|
||
aspect-ratio: 1;
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
position: relative;
|
||
|
||
.photo-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
|
||
&.photo-blurred {
|
||
filter: blur(6px);
|
||
transform: scale(1.02);
|
||
}
|
||
}
|
||
|
||
.photo-lock-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(255, 255, 255, 0.4);
|
||
backdrop-filter: blur(3px);
|
||
}
|
||
|
||
.photo-blur {
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(135deg, #e0e0e0 0%, #c0c0c0 100%);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.blur-text {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 介绍内容
|
||
.intro-content {
|
||
text {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
line-height: 1.8;
|
||
}
|
||
}
|
||
|
||
// 择偶要求网格
|
||
.requirement-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
|
||
.req-item {
|
||
width: 50%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin-bottom: 20rpx;
|
||
|
||
.label {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
}
|
||
}
|
||
|
||
.req-extra {
|
||
padding-top: 16rpx;
|
||
border-top: 1rpx solid #f0f0f0;
|
||
|
||
.label {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
display: block;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.value {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
|
||
// 更多信息网格
|
||
.more-info-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
|
||
.more-item {
|
||
width: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
&.full-width {
|
||
width: 100%;
|
||
}
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 父母信息网格
|
||
.parent-info-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
|
||
.parent-item {
|
||
width: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 联系方式行
|
||
.contact-row {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
flex: 1;
|
||
}
|
||
|
||
.copy-btn {
|
||
padding: 12rpx 24rpx;
|
||
font-size: 24rpx;
|
||
color: #fff;
|
||
background: #ff6b6b;
|
||
border-radius: 20rpx;
|
||
border: none;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 底部提示
|
||
.bottom-tip {
|
||
text-align: center;
|
||
padding: 40rpx 0;
|
||
|
||
text {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
line-height: 1.8;
|
||
}
|
||
}
|
||
|
||
// 底部操作栏
|
||
.bottom-actions {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx 24rpx;
|
||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||
background: #fff;
|
||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
gap: 20rpx;
|
||
|
||
button {
|
||
flex: 1;
|
||
height: 88rpx;
|
||
line-height: 88rpx;
|
||
font-size: 30rpx;
|
||
border-radius: 16rpx;
|
||
border: none;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
|
||
.btn-greet {
|
||
background: #4cd964;
|
||
color: #fff;
|
||
}
|
||
|
||
.btn-favorite {
|
||
background: #f5d742;
|
||
color: #fff;
|
||
|
||
&.active {
|
||
background: #e5c732;
|
||
}
|
||
}
|
||
|
||
.btn-call {
|
||
background: #ff9a9a;
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
// 拨打电话弹窗
|
||
.phone-popup-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
z-index: 999;
|
||
display: flex;
|
||
align-items: flex-end;
|
||
justify-content: center;
|
||
}
|
||
|
||
.phone-popup {
|
||
width: 100%;
|
||
background: #fff;
|
||
border-radius: 24rpx 24rpx 0 0;
|
||
padding: 40rpx;
|
||
padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
|
||
|
||
.phone-popup-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 40rpx;
|
||
|
||
.phone-popup-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.phone-popup-close {
|
||
font-size: 48rpx;
|
||
color: #999;
|
||
line-height: 1;
|
||
}
|
||
}
|
||
|
||
.phone-popup-content {
|
||
text-align: center;
|
||
padding: 40rpx 0;
|
||
|
||
.phone-number {
|
||
font-size: 56rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
letter-spacing: 4rpx;
|
||
}
|
||
}
|
||
|
||
.phone-popup-btn {
|
||
width: 100%;
|
||
height: 96rpx;
|
||
line-height: 96rpx;
|
||
background: #4cd964;
|
||
color: #fff;
|
||
font-size: 34rpx;
|
||
border-radius: 48rpx;
|
||
border: none;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
|
||
&[disabled] {
|
||
opacity: 0.5;
|
||
}
|
||
}
|
||
}
|
||
</style>
|