xiangyixiangqin/miniapp/components/UserCard/index.vue
2026-03-22 17:30:44 +08:00

487 lines
10 KiB
Vue

<template>
<view class="user-card" @click="handleCardClick">
<!-- 今天看过标识 -->
<view v-if="viewedToday" class="viewed-tag">
<text>今天看过</text>
</view>
<!-- 不公开照片样式:两列信息布局 -->
<view v-if="!isPhotoPublic || !firstPhoto" class="card-no-photo">
<!-- 标题行 -->
<view class="title-row">
<text class="gender-year">{{ genderText }} · {{ birthYear }}年</text>
<view class="title-tags">
<image v-if="isMember && memberIconUrl" class="member-icon" :src="memberIconUrl" mode="heightFix" />
<text v-else-if="isMember" class="tag tag-member">会员</text>
<text v-if="isRealName" class="tag tag-realname">已实名</text>
</view>
</view>
<!-- 信息区域:两列布局 -->
<view class="info-grid">
<view class="info-item">
<text class="label">年龄</text>
<text class="value">{{ age }}岁</text>
</view>
<view class="info-item">
<text class="label">身高</text>
<text class="value">{{ height }}cm</text>
</view>
<view class="info-item">
<text class="label">学历</text>
<text class="value">{{ educationName }}</text>
</view>
<view class="info-item">
<text class="label">体重</text>
<text class="value">{{ weight ? 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">{{ occupation || '未填写' }}</text>
</view>
<view class="info-item">
<text class="label">现居</text>
<text class="value">{{ workCity || '未填写' }}</text>
</view>
<view class="info-item">
<text class="label">家乡</text>
<text class="value">{{ hometown || '未填写' }}</text>
</view>
</view>
<!-- 简介 -->
<view class="intro-section" v-if="intro">
<text class="intro-text">{{ intro }}</text>
</view>
<!-- 操作按钮 -->
<view class="action-buttons" @click.stop>
<button class="btn-detail" @click="handleCardClick">查看详细资料</button>
<button class="btn-contact" @click="handleContact">联系对方</button>
</view>
</view>
<!-- 公开照片样式:左信息右照片 -->
<view v-else class="card-with-photo">
<!-- 标题行 -->
<view class="title-row">
<text class="gender-year">{{ genderText }} · {{ birthYear }}年</text>
<view class="title-tags">
<image v-if="isMember && memberIconUrl" class="member-icon" :src="memberIconUrl" mode="heightFix" />
<text v-else-if="isMember" class="tag tag-member">会员</text>
<text v-if="isRealName" class="tag tag-realname">已实名</text>
</view>
</view>
<!-- 内容区域 -->
<view class="content-row">
<!-- 左侧信息 -->
<view class="info-left">
<view class="info-item">
<text class="label">现居</text>
<text class="value">{{ workCity || '未填写' }}</text>
</view>
<view class="info-item">
<text class="label">学历</text>
<text class="value">{{ educationName }}</text>
</view>
<view class="info-item">
<text class="label">身高</text>
<text class="value">{{ height }}cm</text>
</view>
<view class="info-item">
<text class="label">职业</text>
<text class="value">{{ occupation || '未填写' }}</text>
</view>
</view>
<!-- 右侧照片 -->
<view class="photo-right">
<image class="user-photo" :src="fullPhotoUrl" mode="aspectFill" />
</view>
</view>
<!-- 简介 -->
<view class="intro-section" v-if="intro">
<text class="intro-text">{{ intro }}</text>
</view>
<!-- 操作按钮 -->
<view class="action-buttons" @click.stop>
<button class="btn-detail" @click="handleCardClick">查看详细资料</button>
<button class="btn-contact" @click="handleContact">联系对方</button>
</view>
</view>
</view>
</template>
<script>
import {
getFullImageUrl
} from '@/utils/image.js'
import {
useConfigStore
} from '@/store/config.js'
export default {
name: 'UserCard',
props: {
userId: {
type: Number,
required: true
},
nickname: {
type: String,
default: ''
},
avatar: {
type: String,
default: ''
},
gender: {
type: Number,
default: 0
},
age: {
type: Number,
default: 0
},
birthYear: {
type: Number,
default: 0
},
workCity: {
type: String,
default: ''
},
hometown: {
type: String,
default: ''
},
height: {
type: Number,
default: 0
},
weight: {
type: Number,
default: 0
},
education: {
type: Number,
default: 0
},
educationName: {
type: String,
default: ''
},
occupation: {
type: String,
default: ''
},
monthlyIncome: {
type: Number,
default: 0
},
intro: {
type: String,
default: ''
},
isMember: {
type: Boolean,
default: false
},
memberLevel: {
type: Number,
default: 0
},
isRealName: {
type: Boolean,
default: false
},
isPhotoPublic: {
type: Boolean,
default: true
},
firstPhoto: {
type: String,
default: ''
},
viewedToday: {
type: Boolean,
default: false
}
},
emits: ['click', 'contact'],
computed: {
genderText() {
return this.gender === 1 ? '男' : '女'
},
fullPhotoUrl() {
return getFullImageUrl(this.firstPhoto)
},
incomeText() {
if (!this.monthlyIncome) return '未填写'
if (this.monthlyIncome < 5000) return '5千以下'
if (this.monthlyIncome < 8000) return '5千~8千/月'
if (this.monthlyIncome < 10000) return '8千~1万/月'
if (this.monthlyIncome < 15000) return '1万~1.5万/月'
if (this.monthlyIncome < 20000) return '1.5万~2万/月'
if (this.monthlyIncome < 30000) return '2万~3万/月'
return '3万以上/月'
},
memberIconUrl() {
if (!this.isMember || !this.memberLevel) return ''
const configStore = useConfigStore()
const iconUrl = configStore.getMemberIcon(this.memberLevel)
return iconUrl ? getFullImageUrl(iconUrl) : ''
}
},
methods: {
handleCardClick() {
this.$emit('click', this.userId)
},
handleContact() {
this.$emit('contact', this.userId)
}
}
}
</script>
<style lang="scss" scoped>
.user-card {
background: linear-gradient(to bottom, #FFEBEA 0%, #FFFFFF 100%);
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
margin-bottom: 24rpx;
position: relative;
padding: 32rpx;
}
// 今天看过标签
.viewed-tag {
position: absolute;
top: 0;
right: 0;
background: rgba(255, 180, 180, 0.65);
padding: 15rpx 24rpx;
border-radius: 0 24rpx 0 24rpx;
display: flex;
align-items: center;
justify-content: center;
text {
color: #fff;
font-size: 28rpx;
font-weight: 600;
line-height: 1;
}
}
// 标题行
.title-row {
margin-bottom: 24rpx;
display: flex;
flex-direction: row;
.gender-year {
font-size: 42rpx;
font-weight: 600;
color: #ff6b6b;
display: inline;
margin-right: 16rpx;
}
.title-tags {
display: flex;
flex-direction: row;
align-items: center;
gap: 8rpx;
vertical-align: middle;
.member-icon {
width: auto;
height: 42rpx;
vertical-align: middle;
}
.tag {
display: inline-block;
font-size: 26rpx;
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;
}
}
}
}
// 不公开照片样式
.card-no-photo {
.info-grid {
display: flex;
flex-wrap: wrap;
.info-item {
width: 50%;
display: flex;
align-items: center;
margin-bottom: 20rpx;
.label {
font-size: 32rpx;
color: #999;
width: 80rpx;
flex-shrink: 0;
}
.value {
font-size: 32rpx;
color: #333;
font-weight: 600;
margin-left: 16rpx;
}
}
}
}
// 公开照片样式
.card-with-photo {
.content-row {
display: flex;
justify-content: space-between;
.info-left {
flex: 1;
.info-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 32rpx;
color: #999;
width: 80rpx;
flex-shrink: 0;
}
.value {
font-size: 32rpx;
color: #333;
font-weight: 600;
margin-left: 16rpx;
}
}
}
.photo-right {
width: 200rpx;
height: 200rpx;
margin-left: 24rpx;
flex-shrink: 0;
.user-photo {
width: 100%;
height: 100%;
border-radius: 16rpx;
}
}
}
}
// 简介区域
.intro-section {
margin-top: 24rpx;
padding-top: 24rpx;
border-top: 1rpx solid #f5f5f5;
.intro-text {
font-size: 30rpx;
color: #333;
line-height: 1.6;
font-weight: 600;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
}
// 操作按钮
.action-buttons {
display: flex;
gap: 24rpx;
margin-top: 32rpx;
button {
flex: 1;
height: 80rpx;
line-height: 80rpx;
font-size: 32rpx;
border-radius: 40rpx;
border: none;
&::after {
border: none;
}
}
.btn-detail {
background: #fff;
color: #333;
border: 2rpx solid #FFCBCB;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: -2rpx;
left: -2rpx;
right: -2rpx;
bottom: -2rpx;
background: linear-gradient(to right, #FFCBCB, #FF7B7B);
z-index: -1;
border-radius: 40rpx;
}
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #fff;
z-index: -1;
border-radius: 38rpx;
}
}
.btn-contact {
background: linear-gradient(to right, #FFBDC2, #FF8A93);
color: #fff;
}
}
</style>