xiangyixiangqin/miniapp/components/UserCard/index.vue
2026-01-02 18:00:49 +08:00

265 lines
5.0 KiB
Vue

<template>
<view class="user-card" @click="handleCardClick">
<!-- 照片区域 -->
<view class="photo-wrapper">
<image
v-if="isPhotoPublic && firstPhoto"
class="user-photo"
:src="firstPhoto"
mode="aspectFill"
/>
<view v-else class="photo-placeholder">
<view class="blur-overlay"></view>
<text class="private-text">私密照片</text>
</view>
<!-- 徽章区域 -->
<view class="badges">
<view v-if="isMember" class="badge member-badge">会员</view>
<view v-if="isRealName" class="badge realname-badge">已实名</view>
</view>
<!-- 今天看过标识 -->
<view v-if="viewedToday" class="viewed-today">
<text>今天看过</text>
</view>
</view>
<!-- 用户信息区域 -->
<view class="user-info">
<view class="info-row">
<text class="nickname">{{ nickname }}</text>
<text class="age">{{ age }}岁</text>
</view>
<view class="info-row secondary">
<text class="location">{{ workCity }}</text>
<text class="divider">|</text>
<text class="height">{{ height }}cm</text>
</view>
<view class="info-row secondary">
<text class="education">{{ educationName }}</text>
<text class="divider">|</text>
<text class="occupation">{{ occupation }}</text>
</view>
</view>
<!-- 操作按钮区域 -->
<view class="action-buttons" @click.stop>
<button class="btn-contact" @click="handleContact">联系TA</button>
</view>
</view>
</template>
<script>
export default {
name: 'UserCard',
props: {
userId: {
type: Number,
required: true
},
nickname: {
type: String,
default: ''
},
avatar: {
type: String,
default: ''
},
age: {
type: Number,
default: 0
},
workCity: {
type: String,
default: ''
},
height: {
type: Number,
default: 0
},
education: {
type: Number,
default: 0
},
educationName: {
type: String,
default: ''
},
occupation: {
type: String,
default: ''
},
isMember: {
type: Boolean,
default: false
},
isRealName: {
type: Boolean,
default: false
},
isPhotoPublic: {
type: Boolean,
default: true
},
firstPhoto: {
type: String,
default: ''
},
viewedToday: {
type: Boolean,
default: false
}
},
emits: ['click', 'contact'],
methods: {
handleCardClick() {
this.$emit('click', this.userId)
},
handleContact() {
this.$emit('contact', this.userId)
}
}
}
</script>
<style lang="scss" scoped>
.user-card {
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
margin-bottom: 20rpx;
}
.photo-wrapper {
position: relative;
width: 100%;
height: 400rpx;
.user-photo {
width: 100%;
height: 100%;
}
.photo-placeholder {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #e0e0e0 0%, #c0c0c0 100%);
display: flex;
align-items: center;
justify-content: center;
position: relative;
.blur-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
}
.private-text {
position: relative;
z-index: 1;
color: #666;
font-size: 28rpx;
}
}
.badges {
position: absolute;
top: 16rpx;
left: 16rpx;
display: flex;
gap: 8rpx;
.badge {
padding: 4rpx 12rpx;
border-radius: 8rpx;
font-size: 22rpx;
color: #fff;
}
.member-badge {
background: linear-gradient(135deg, #ffd700 0%, #ffb800 100%);
}
.realname-badge {
background: linear-gradient(135deg, #4cd964 0%, #34c759 100%);
}
}
.viewed-today {
position: absolute;
bottom: 16rpx;
right: 16rpx;
background: rgba(0, 0, 0, 0.5);
padding: 4rpx 12rpx;
border-radius: 8rpx;
text {
color: #fff;
font-size: 22rpx;
}
}
}
.user-info {
padding: 20rpx;
.info-row {
display: flex;
align-items: center;
margin-bottom: 8rpx;
&:last-child {
margin-bottom: 0;
}
&.secondary {
color: #999;
font-size: 26rpx;
}
.nickname {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-right: 12rpx;
}
.age {
font-size: 28rpx;
color: #ff6b6b;
}
.divider {
margin: 0 8rpx;
color: #ddd;
}
}
}
.action-buttons {
padding: 0 20rpx 20rpx;
.btn-contact {
width: 100%;
height: 72rpx;
line-height: 72rpx;
background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
color: #fff;
font-size: 28rpx;
border-radius: 36rpx;
border: none;
&::after {
border: none;
}
}
}
</style>