607 lines
14 KiB
Vue
607 lines
14 KiB
Vue
<template>
|
||
<view class="interact-page">
|
||
<!-- 页面加载状态 -->
|
||
<Loading type="page" :loading="pageLoading" />
|
||
|
||
<!-- 顶部背景图 -->
|
||
<view class="top-bg">
|
||
<image src="/static/title_bg.png" mode="aspectFill" class="bg-img" />
|
||
</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">{{ activeTab === 'favoritedMe' ? '收藏我' : '我收藏的' }}</text>
|
||
<view class="navbar-placeholder"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Tab切换 -->
|
||
<view class="tab-header" :style="{ top: (statusBarHeight + 44) + 'px' }">
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'favoritedMe' }"
|
||
@click="switchTab('favoritedMe')"
|
||
>
|
||
<text>收藏我</text>
|
||
</view>
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'myFavorite' }"
|
||
@click="switchTab('myFavorite')"
|
||
>
|
||
<text>我收藏的</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 可滚动内容区域 -->
|
||
<scroll-view
|
||
class="content-scroll"
|
||
scroll-y
|
||
:style="{
|
||
top: (statusBarHeight + 44 + 80) + 'px',
|
||
height: 'calc(100vh - ' + (statusBarHeight + 44 + 80) + 'px)'
|
||
}"
|
||
@scrolltolower="handleScrollToLower"
|
||
>
|
||
<!-- 用户列表 -->
|
||
<view class="user-list" v-if="list.length > 0">
|
||
<view
|
||
class="user-card"
|
||
v-for="item in list"
|
||
:key="item.id || item.userId"
|
||
@click="handleUserClick(item.userId)"
|
||
>
|
||
<!-- 时间信息 -->
|
||
<view class="time-row">
|
||
<text class="time-text">{{ formatViewTime(item.createTime) }}{{ activeTab === 'favoritedMe' ? '收藏了我' : '收藏' }}</text>
|
||
</view>
|
||
|
||
<!-- 性别年份 -->
|
||
<view class="title-row">
|
||
<text class="gender-year">{{ item.gender === 1 ? '男' : '女' }} · {{ item.birthYear || getYearFromAge(item.age) }}年</text>
|
||
</view>
|
||
|
||
<!-- 内容区域 -->
|
||
<view class="content-row">
|
||
<!-- 左侧信息 -->
|
||
<view class="info-section" :class="{ 'full-width': !item.isPhotoPublic || !item.firstPhoto }">
|
||
<view class="info-grid">
|
||
<view class="info-item">
|
||
<text class="label">现居</text>
|
||
<text class="value">{{ item.workCity || '未填写' }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">身高</text>
|
||
<text class="value">{{ item.height ? item.height + 'cm' : '未填写' }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">学历</text>
|
||
<text class="value">{{ item.educationName || '未填写' }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">体重</text>
|
||
<text class="value">{{ item.weight ? item.weight + 'kg' : '未填写' }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">职业</text>
|
||
<text class="value">{{ item.occupation || '未填写' }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">家乡</text>
|
||
<text class="value">{{ item.hometown || '未填写' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 右侧照片 -->
|
||
<view class="photo-section" v-if="item.isPhotoPublic && item.firstPhoto">
|
||
<image class="user-photo" :src="getFullUrl(item.firstPhoto)" mode="aspectFill" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 简介 -->
|
||
<view class="intro-section" v-if="item.intro">
|
||
<text class="intro-text">{{ item.intro }}</text>
|
||
</view>
|
||
|
||
<!-- 操作按钮 -->
|
||
<view class="action-buttons" @click.stop>
|
||
<button class="btn-detail" @click="handleUserClick(item.userId)">查看详细资料</button>
|
||
<button class="btn-contact" @click="handleContact(item.userId)">联系对方</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<Empty
|
||
v-else-if="!listLoading"
|
||
:text="activeTab === 'favoritedMe' ? '暂无人收藏你' : '你还没有收藏别人'"
|
||
buttonText="去相亲"
|
||
buttonUrl="/pages/index/index"
|
||
/>
|
||
|
||
<!-- 加载更多 -->
|
||
<Loading
|
||
type="more"
|
||
:loading="listLoading"
|
||
:noMore="noMoreData && list.length > 0"
|
||
/>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { getFavoritedMe, getMyFavorite } from '@/api/interact.js'
|
||
import { getFullImageUrl } from '@/utils/image.js'
|
||
import { useUserStore } from '@/store/user.js'
|
||
import { useConfigStore } from '@/store/config.js'
|
||
import Loading from '@/components/Loading/index.vue'
|
||
import Empty from '@/components/Empty/index.vue'
|
||
|
||
export default {
|
||
name: 'FavoritedMePage',
|
||
components: {
|
||
Loading,
|
||
Empty
|
||
},
|
||
setup() {
|
||
const userStore = useUserStore()
|
||
const configStore = useConfigStore()
|
||
|
||
// 状态栏高度
|
||
const statusBarHeight = ref(20)
|
||
|
||
// 页面状态
|
||
const pageLoading = ref(true)
|
||
const listLoading = ref(false)
|
||
const noMoreData = ref(false)
|
||
const activeTab = ref('favoritedMe')
|
||
|
||
// 数据
|
||
const list = ref([])
|
||
const pageIndex = ref(1)
|
||
const pageSize = 20
|
||
|
||
// 从 configStore 获取默认头像
|
||
const defaultAvatar = computed(() => configStore.defaultAvatar || '/static/logo.png')
|
||
|
||
// 获取系统信息
|
||
const getSystemInfo = () => {
|
||
uni.getSystemInfo({
|
||
success: (res) => {
|
||
statusBarHeight.value = res.statusBarHeight || 20
|
||
}
|
||
})
|
||
}
|
||
|
||
// 返回上一页
|
||
const handleBack = () => {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
// 获取完整图片URL
|
||
const getFullUrl = (url) => {
|
||
return getFullImageUrl(url)
|
||
}
|
||
|
||
// 从年龄计算出生年份
|
||
const getYearFromAge = (age) => {
|
||
if (!age) return ''
|
||
return new Date().getFullYear() - age
|
||
}
|
||
|
||
// 格式化时间 - 显示为"今天15:21"或"昨天15:21"或"01-20 15:21"
|
||
const formatViewTime = (timeStr) => {
|
||
if (!timeStr) return ''
|
||
|
||
const date = new Date(timeStr)
|
||
const now = new Date()
|
||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
|
||
const targetDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
||
|
||
const hours = String(date.getHours()).padStart(2, '0')
|
||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||
const timeStr2 = `${hours}:${minutes}`
|
||
|
||
if (targetDate.getTime() === today.getTime()) {
|
||
return `今天${timeStr2}`
|
||
} else if (targetDate.getTime() === yesterday.getTime()) {
|
||
return `昨天${timeStr2}`
|
||
} else {
|
||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
const day = String(date.getDate()).padStart(2, '0')
|
||
return `${month}-${day} ${timeStr2}`
|
||
}
|
||
}
|
||
|
||
// 加载列表
|
||
const loadList = async (refresh = false) => {
|
||
if (refresh) {
|
||
pageIndex.value = 1
|
||
noMoreData.value = false
|
||
}
|
||
|
||
if (noMoreData.value && !refresh) return
|
||
|
||
listLoading.value = true
|
||
try {
|
||
const api = activeTab.value === 'favoritedMe' ? getFavoritedMe : getMyFavorite
|
||
const res = await api(pageIndex.value, pageSize)
|
||
|
||
if (res && (res.success || res.code === 0)) {
|
||
const newList = res.data?.items || []
|
||
|
||
if (refresh) {
|
||
list.value = newList
|
||
} else {
|
||
list.value = [...list.value, ...newList]
|
||
}
|
||
|
||
// 判断是否还有更多数据
|
||
if (newList.length < pageSize) {
|
||
noMoreData.value = true
|
||
} else {
|
||
pageIndex.value++
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('加载列表失败:', error)
|
||
} finally {
|
||
listLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 初始化页面
|
||
const initPage = async () => {
|
||
pageLoading.value = true
|
||
try {
|
||
getSystemInfo()
|
||
await loadList(true)
|
||
} finally {
|
||
pageLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 切换Tab
|
||
const switchTab = (tab) => {
|
||
if (activeTab.value === tab) return
|
||
activeTab.value = tab
|
||
|
||
// 重新加载数据
|
||
loadList(true)
|
||
}
|
||
|
||
// 滚动到底部加载更多
|
||
const handleScrollToLower = () => {
|
||
if (!noMoreData.value && !listLoading.value) {
|
||
loadList()
|
||
}
|
||
}
|
||
|
||
// 点击用户
|
||
const handleUserClick = (userId) => {
|
||
uni.navigateTo({ url: `/pages/profile/detail?userId=${userId}` })
|
||
}
|
||
|
||
// 联系用户
|
||
const handleContact = (userId) => {
|
||
// 检查是否完善资料
|
||
if (!userStore.isProfileCompleted) {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '请先完善您的资料,才能联系对方',
|
||
confirmText: '去完善',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.navigateTo({ url: '/pages/profile/edit' })
|
||
}
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
uni.navigateTo({ url: `/pages/chat/index?targetUserId=${userId}` })
|
||
}
|
||
|
||
onMounted(() => {
|
||
initPage()
|
||
})
|
||
|
||
return {
|
||
statusBarHeight,
|
||
pageLoading,
|
||
listLoading,
|
||
noMoreData,
|
||
activeTab,
|
||
list,
|
||
defaultAvatar,
|
||
handleBack,
|
||
switchTab,
|
||
formatViewTime,
|
||
getFullUrl,
|
||
getYearFromAge,
|
||
handleUserClick,
|
||
handleContact,
|
||
handleScrollToLower,
|
||
loadList
|
||
}
|
||
},
|
||
// 下拉刷新
|
||
onPullDownRefresh() {
|
||
this.loadList && this.loadList(true).finally(() => {
|
||
uni.stopPullDownRefresh()
|
||
})
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.interact-page {
|
||
height: 100vh;
|
||
background-color: #f5f5f5;
|
||
overflow: hidden;
|
||
}
|
||
|
||
// 顶部背景图
|
||
.top-bg {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 400rpx;
|
||
z-index: 0;
|
||
|
||
.bg-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
// 自定义导航栏
|
||
.custom-navbar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 200;
|
||
|
||
.navbar-content {
|
||
position: relative;
|
||
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: #333;
|
||
font-weight: 300;
|
||
}
|
||
}
|
||
|
||
.navbar-title {
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.navbar-placeholder {
|
||
width: 80rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Tab切换
|
||
.tab-header {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
display: flex;
|
||
padding: 20rpx 40rpx;
|
||
gap: 24rpx;
|
||
|
||
.tab-item {
|
||
padding: 20rpx 40rpx;
|
||
border-radius: 40rpx;
|
||
background-color: #fff;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||
|
||
text {
|
||
font-size: 30rpx;
|
||
color: #2F2F2F;
|
||
}
|
||
|
||
&.active {
|
||
background-color: #FFF0F0;
|
||
position: relative;
|
||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 107, 0.2);
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
border-radius: 40rpx;
|
||
padding: 2rpx;
|
||
background: linear-gradient(to right, #FFCBCB, #FF7B7B);
|
||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask-composite: exclude;
|
||
}
|
||
|
||
text {
|
||
color: #FF5F5F;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 可滚动内容区域
|
||
.content-scroll {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 1;
|
||
}
|
||
|
||
// 用户列表
|
||
.user-list {
|
||
padding: 20rpx;
|
||
}
|
||
|
||
// 用户卡片
|
||
.user-card {
|
||
background: linear-gradient(to bottom, #FFEBEA 0%, #FFFFFF 100%);
|
||
border-radius: 24rpx;
|
||
padding: 32rpx;
|
||
margin-bottom: 24rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
// 时间行
|
||
.time-row {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.time-text {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
|
||
// 标题行
|
||
.title-row {
|
||
margin-bottom: 24rpx;
|
||
|
||
.gender-year {
|
||
font-size: 40rpx;
|
||
font-weight: 600;
|
||
color: #ff6b6b;
|
||
}
|
||
}
|
||
|
||
// 内容区域
|
||
.content-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
// 信息区域
|
||
.info-section {
|
||
flex: 1;
|
||
|
||
&.full-width {
|
||
.info-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.info-item {
|
||
width: 50%;
|
||
}
|
||
}
|
||
}
|
||
|
||
.info-grid {
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
width: 80rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-left: 16rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 照片区域
|
||
.photo-section {
|
||
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: 28rpx;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
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: 30rpx;
|
||
border-radius: 40rpx;
|
||
border: none;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
|
||
.btn-detail {
|
||
background: #fff;
|
||
color: #333;
|
||
border: 2rpx solid #FFCBCB;
|
||
}
|
||
|
||
.btn-contact {
|
||
background: linear-gradient(to right, #FFBDC2, #FF8A93);
|
||
color: #fff;
|
||
}
|
||
}
|
||
</style>
|