xiangyixiangqin/miniapp/pages/profile/detail.vue
2026-01-21 19:36:36 +08:00

1227 lines
29 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>
<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 || '/static/default-avatar.png'"
mode="aspectFill"
/>
</view>
<view class="header-right">
<view class="name-row">
<text class="nickname">{{ userDetail.nickname }}</text>
<text class="relationship">({{ relationshipText }})</text>
<view class="header-tags">
<text v-if="userDetail.isRealName" class="tag tag-realname">已实名</text>
<text v-if="userDetail.isMember" class="tag tag-member">会员</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
v-if="userDetail.isPhotoPublic || isUnlocked"
class="photo-image"
:src="photo.photoUrl"
mode="aspectFill"
/>
<view v-else class="photo-blur">
<text class="blur-text">私密</text>
</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" v-if="userDetail.requirement">
<view class="section-title">孩子的择偶标准</view>
<view class="requirement-grid">
<view class="req-item" v-if="requirementAgeText">
<text class="label">年龄范围</text>
<text class="value">{{ requirementAgeText }}</text>
</view>
<view class="req-item" v-if="requirementHeightText">
<text class="label">身高要求</text>
<text class="value">{{ requirementHeightText }}</text>
</view>
<view class="req-item" v-if="requirementEducationText">
<text class="label">学历要求</text>
<text class="value">{{ requirementEducationText }}</text>
</view>
<view class="req-item" v-if="requirementCityText">
<text class="label">城市要求</text>
<text class="value">{{ requirementCityText }}</text>
</view>
<view class="req-item" v-if="requirementIncomeText">
<text class="label">收入范围</text>
<text class="value">{{ requirementIncomeText }}</text>
</view>
</view>
<view class="req-extra" v-if="requirementExtraText">
<text class="label">其他</text>
<text class="value">{{ requirementExtraText }}</text>
</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.isLivingAlone ? '是' : '否' }}</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.isParentRetired ? '已退休' : '未退休' }}</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.parentHasHouse ? '有' : '无' }}</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 { 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 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 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.join('、') || ''
})
const requirementIncomeText = computed(() => {
const req = userDetail.value?.requirement
if (!req || !req.monthlyIncomeMin) return ''
return incomeMap[req.monthlyIncomeMin] || ''
})
const requirementExtraText = computed(() => {
const req = userDetail.value?.requirement
if (!req) return ''
const extras = []
if (req.houseStatus && req.houseStatus.length > 0) {
extras.push('房产要求: ' + req.houseStatus.map(h => houseMap[h]).filter(Boolean).join('、'))
}
if (req.carStatus && req.carStatus.length > 0) {
extras.push('车辆要求: ' + req.carStatus.map(c => carMap[c]).filter(Boolean).join('、'))
}
if (req.marriageStatus && req.marriageStatus.length > 0) {
extras.push('婚姻要求: ' + req.marriageStatus.map(m => marriageMap[m]).filter(Boolean).join('、'))
}
return extras.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 = () => {
// 检查是否登录
if (!userStore.isLoggedIn) {
uni.showModal({
title: '提示',
content: '请先登录后再联系对方',
confirmText: '去登录',
success: (res) => {
if (res.confirm) {
uni.navigateTo({ url: '/pages/login/index' })
}
}
})
return
}
if (!userStore.isProfileCompleted) {
showProfilePopup.value = true
return
}
if (isUnlocked.value) {
uni.navigateTo({ url: `/pages/chat/index?targetUserId=${userId.value}` })
} 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: 64rpx;
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: flex;
gap: 8rpx;
margin-left: 8rpx;
.tag {
font-size: 20rpx;
padding: 4rpx 12rpx;
border-radius: 16rpx;
&.tag-realname {
background: #e8f5e9;
color: #4caf50;
}
&.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;
.photo-image {
width: 100%;
height: 100%;
}
.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>