xiangyixiangqin/miniapp/pages/interact/unlockedMe.vue
2026-01-05 20:57:08 +08:00

384 lines
8.7 KiB
Vue

<template>
<view class="interact-page">
<!-- 页面加载状态 -->
<Loading type="page" :loading="pageLoading" />
<!-- Tab切换 -->
<view class="tab-header">
<view
class="tab-item"
:class="{ active: activeTab === 'unlockedMe' }"
@click="switchTab('unlockedMe')"
>
<text>解锁我</text>
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'myUnlocked' }"
@click="switchTab('myUnlocked')"
>
<text>我解锁的</text>
</view>
</view>
<!-- 用户列表 -->
<view class="user-list" v-if="list.length > 0">
<view
class="user-item"
v-for="item in list"
:key="item.id"
@click="handleUserClick(item.userId)"
>
<!-- 头像 -->
<view class="user-avatar">
<image
class="avatar-image"
:src="item.avatar || defaultAvatar"
mode="aspectFill"
/>
</view>
<!-- 用户信息 -->
<view class="user-info">
<view class="user-header">
<text class="user-nickname">{{ item.nickname }}</text>
<view class="user-badges">
<view v-if="item.isMember" class="badge member-badge">会员</view>
<view v-if="item.isRealName" class="badge realname-badge">已实名</view>
</view>
</view>
<view class="user-detail">
<text>{{ item.age }}岁</text>
<text class="divider">|</text>
<text>{{ item.workCity }}</text>
<text class="divider">|</text>
<text>{{ item.height }}cm</text>
</view>
<view class="user-time">
<text>{{ formatTime(item.createTime) }}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="user-action" @click.stop="handleContact(item.userId)">
<text>联系TA</text>
</view>
</view>
</view>
<!-- 空状态 -->
<Empty
v-else-if="!listLoading"
:text="activeTab === 'unlockedMe' ? '暂无人解锁你' : '你还没有解锁别人'"
buttonText="去相亲"
buttonUrl="/pages/index/index"
/>
<!-- 加载更多 -->
<Loading
type="more"
:loading="listLoading"
:noMore="noMoreData && list.length > 0"
/>
</view>
</template>
<script>
import { ref, onMounted } from 'vue'
import { getUnlockedMe, getMyUnlocked } from '@/api/interact.js'
import { formatTimestamp } from '@/utils/format.js'
import { useUserStore } from '@/store/user.js'
import Loading from '@/components/Loading/index.vue'
import Empty from '@/components/Empty/index.vue'
export default {
name: 'UnlockedMePage',
components: {
Loading,
Empty
},
setup() {
const userStore = useUserStore()
// 页面状态
const pageLoading = ref(true)
const listLoading = ref(false)
const noMoreData = ref(false)
const activeTab = ref('unlockedMe')
// 数据
const list = ref([])
const pageIndex = ref(1)
const pageSize = 20
// 默认头像
const defaultAvatar = '/static/logo.png'
// 加载列表
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 === 'unlockedMe' ? getUnlockedMe : getMyUnlocked
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 {
await loadList(true)
} finally {
pageLoading.value = false
}
}
// 切换Tab
const switchTab = (tab) => {
if (activeTab.value === tab) return
activeTab.value = tab
// 更新导航栏标题
uni.setNavigationBarTitle({
title: tab === 'unlockedMe' ? '解锁我' : '我解锁的'
})
// 重新加载数据
loadList(true)
}
// 格式化时间
const formatTime = (timestamp) => {
return formatTimestamp(timestamp)
}
// 点击用户
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 {
pageLoading,
listLoading,
noMoreData,
activeTab,
list,
defaultAvatar,
switchTab,
formatTime,
handleUserClick,
handleContact,
loadList
}
},
// 下拉刷新
onPullDownRefresh() {
this.loadList && this.loadList(true).finally(() => {
uni.stopPullDownRefresh()
})
},
// 触底加载更多
onReachBottom() {
if (!this.noMoreData && !this.listLoading) {
this.loadList && this.loadList()
}
}
}
</script>
<style lang="scss" scoped>
.interact-page {
min-height: 100vh;
background-color: #f8f8f8;
}
// Tab切换
.tab-header {
display: flex;
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
.tab-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 28rpx 0;
position: relative;
text {
font-size: 30rpx;
color: #666;
}
&.active {
text {
color: #ff6b6b;
font-weight: 600;
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 4rpx;
background-color: #ff6b6b;
border-radius: 2rpx;
}
}
}
}
// 用户列表
.user-list {
padding: 0 20rpx;
.user-item {
display: flex;
align-items: center;
padding: 24rpx;
background-color: #fff;
border-radius: 16rpx;
margin-top: 20rpx;
&:active {
background-color: #f8f8f8;
}
.user-avatar {
width: 120rpx;
height: 120rpx;
margin-right: 24rpx;
flex-shrink: 0;
.avatar-image {
width: 100%;
height: 100%;
border-radius: 16rpx;
}
}
.user-info {
flex: 1;
overflow: hidden;
.user-header {
display: flex;
align-items: center;
margin-bottom: 8rpx;
.user-nickname {
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-right: 12rpx;
}
.user-badges {
display: flex;
gap: 8rpx;
.badge {
padding: 2rpx 8rpx;
border-radius: 6rpx;
font-size: 20rpx;
color: #fff;
}
.member-badge {
background: linear-gradient(135deg, #ffd700 0%, #ffb800 100%);
}
.realname-badge {
background: linear-gradient(135deg, #4cd964 0%, #34c759 100%);
}
}
}
.user-detail {
font-size: 26rpx;
color: #666;
margin-bottom: 8rpx;
.divider {
margin: 0 8rpx;
color: #ddd;
}
}
.user-time {
font-size: 24rpx;
color: #999;
}
}
.user-action {
padding: 12rpx 24rpx;
background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
border-radius: 30rpx;
flex-shrink: 0;
text {
font-size: 26rpx;
color: #fff;
}
}
}
}
</style>