@ -0,0 +1,100 @@ | |||
<template> | |||
<view class="waterfall-container"> | |||
<view class="waterfall-columns"> | |||
<!-- 左列 --> | |||
<view class="waterfall-column"> | |||
<waterfallItem | |||
v-for="(item, index) in leftColumnData" | |||
:key="'left_' + index" | |||
:item="item" | |||
@click="handleItemClick" | |||
@like="handleItemLike" | |||
/> | |||
</view> | |||
<!-- 右列 --> | |||
<view class="waterfall-column"> | |||
<waterfallItem | |||
v-for="(item, index) in rightColumnData" | |||
:key="'right_' + index" | |||
:item="item" | |||
@click="handleItemClick" | |||
@like="handleItemLike" | |||
/> | |||
</view> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
import waterfallItem from './waterfallItem.vue' | |||
export default { | |||
components: { | |||
waterfallItem | |||
}, | |||
props: { | |||
list: { | |||
type: Array, | |||
default: () => [] | |||
} | |||
}, | |||
data() { | |||
return { | |||
leftColumnData: [], | |||
rightColumnData: [] | |||
} | |||
}, | |||
watch: { | |||
list: { | |||
handler(newList) { | |||
this.distributeItems(newList) | |||
}, | |||
immediate: true | |||
} | |||
}, | |||
methods: { | |||
// 将数据分配到左右两列 | |||
distributeItems(items) { | |||
this.leftColumnData = [] | |||
this.rightColumnData = [] | |||
items.forEach((item, index) => { | |||
// 简单的奇偶分配,也可以根据内容高度智能分配 | |||
if (index % 2 === 0) { | |||
this.leftColumnData.push(item) | |||
} else { | |||
this.rightColumnData.push(item) | |||
} | |||
}) | |||
}, | |||
// 处理点击事件 | |||
handleItemClick(item) { | |||
this.$emit('item-click', item) | |||
}, | |||
// 处理点赞事件 | |||
handleItemLike(item) { | |||
this.$emit('item-like', item) | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang="scss"> | |||
.waterfall-container { | |||
padding: 20rpx; | |||
.waterfall-columns { | |||
display: flex; | |||
gap: 20rpx; | |||
.waterfall-column { | |||
flex: 1; | |||
display: flex; | |||
flex-direction: column; | |||
} | |||
} | |||
} | |||
</style> |
@ -0,0 +1,297 @@ | |||
<template> | |||
<view class="waterfall-item" @click="handleClick"> | |||
<!-- 主图片 --> | |||
<view class="main-image" v-if="mainImage"> | |||
<image :src="mainImage" mode="aspectFill" @click.stop="previewImage([mainImage])"></image> | |||
<!-- 分类标签 --> | |||
<view class="category-tag" v-if="item.classId_dictText"> | |||
#{{ item.classId_dictText }} | |||
</view> | |||
</view> | |||
<!-- 内容区域 --> | |||
<view class="content"> | |||
<!-- 标题/内容 --> | |||
<view class="title" v-if="item.title" v-html="formatContent(item.title)"></view> | |||
<!-- 地址信息 --> | |||
<view class="address" v-if="item.address"> | |||
<uv-icon name="map-pin" size="24rpx" color="#999"></uv-icon> | |||
<text>{{ item.address }}</text> | |||
</view> | |||
<!-- 用户信息 --> | |||
<view class="user-info"> | |||
<view class="user-avatar"> | |||
<image :src="item.userImage" mode="aspectFill" @click.stop="previewImage([item.userImage])"></image> | |||
</view> | |||
<view class="user-details"> | |||
<view class="username">{{ item.userName }}</view> | |||
<view class="user-tags"> | |||
<text class="tag" v-if="item.sex" :style="{'background-color': sexColors[item.sex] || '#999'}">{{ item.sex }}</text> | |||
<text class="tag" v-if="item.yearDate">{{ item.yearDate }}</text> | |||
<text class="auth-tag" v-if="item.isContent">{{ item.isContent }}</text> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 互动数据 --> | |||
<view class="interaction"> | |||
<view class="interaction-item"> | |||
<uv-icon name="eye" size="24rpx" color="#999"></uv-icon> | |||
<text>{{ item.isBrowse || 0 }}</text> | |||
</view> | |||
<view class="interaction-item"> | |||
<uv-icon name="chat" size="24rpx" color="#999"></uv-icon> | |||
<text>{{ item.isComment || 0 }}</text> | |||
</view> | |||
<view class="interaction-item" @click.stop="handleLike"> | |||
<uv-icon name="thumb-up" size="24rpx" :color="isLiked ? '#ff4757' : '#999'"></uv-icon> | |||
<text :style="{color: isLiked ? '#ff4757' : '#999'}">{{ item.isUp || 0 }}</text> | |||
</view> | |||
<!-- 发布时间 --> | |||
<view class="publish-time"> | |||
{{ formatTime(item.createTime) }} | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
export default { | |||
props: { | |||
item: { | |||
type: Object, | |||
default: () => ({}) | |||
} | |||
}, | |||
data() { | |||
return { | |||
isLiked: false, | |||
sexColors: { | |||
'男': '#4A90E2', | |||
'女': '#FF69B4', | |||
'其他': '#999' | |||
} | |||
} | |||
}, | |||
computed: { | |||
// 主图片 - 优先显示微信图片,然后是第一张图片 | |||
mainImage() { | |||
if (this.item.wxImage) { | |||
return this.item.wxImage | |||
} | |||
if (this.item.image) { | |||
const images = this.item.image.split(',').filter(img => img.trim()) | |||
return images.length > 0 ? images[0] : null | |||
} | |||
return null | |||
} | |||
}, | |||
methods: { | |||
// 处理点击事件 | |||
handleClick() { | |||
this.$emit('click', this.item) | |||
}, | |||
// 处理点赞事件 | |||
handleLike() { | |||
this.isLiked = !this.isLiked | |||
this.$emit('like', this.item) | |||
}, | |||
// 预览图片 | |||
previewImage(urls, current = 0) { | |||
if (!urls || urls.length === 0) return | |||
uni.previewImage({ | |||
urls: urls, | |||
current: current | |||
}) | |||
}, | |||
// 格式化内容 | |||
formatContent(content) { | |||
if (!content) return '' | |||
// 尝试使用全局utils,如果不存在则返回原内容 | |||
if (this.$utils && this.$utils.stringFormatHtml) { | |||
return this.$utils.stringFormatHtml(content) | |||
} | |||
// 简单的HTML处理 | |||
return content.replace(/\n/g, '<br/>') | |||
}, | |||
// 格式化时间 | |||
formatTime(timeStr) { | |||
if (!timeStr) return '' | |||
// 如果已经包含"发布",直接返回 | |||
if (timeStr.includes('发布')) { | |||
return timeStr | |||
} | |||
// 简单的时间格式处理 | |||
const now = new Date() | |||
const time = new Date(timeStr) | |||
const diff = now - time | |||
const days = Math.floor(diff / (1000 * 60 * 60 * 24)) | |||
if (days === 0) { | |||
return '今天' | |||
} else if (days === 1) { | |||
return '昨天' | |||
} else if (days < 7) { | |||
return `${days}天前` | |||
} else { | |||
// 返回月-日格式 | |||
const month = time.getMonth() + 1 | |||
const day = time.getDate() | |||
return `${month}-${day}` | |||
} | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang="scss"> | |||
.waterfall-item { | |||
background-color: #fff; | |||
border-radius: 16rpx; | |||
overflow: hidden; | |||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08); | |||
margin-bottom: 20rpx; | |||
.main-image { | |||
position: relative; | |||
width: 100%; | |||
image { | |||
width: 100%; | |||
height: 400rpx; | |||
object-fit: cover; | |||
} | |||
.category-tag { | |||
position: absolute; | |||
top: 16rpx; | |||
right: 16rpx; | |||
background: rgba(255, 215, 0, 0.9); | |||
color: #333; | |||
padding: 8rpx 16rpx; | |||
border-radius: 20rpx; | |||
font-size: 22rpx; | |||
font-weight: 500; | |||
} | |||
} | |||
.content { | |||
padding: 24rpx; | |||
.title { | |||
font-size: 28rpx; | |||
line-height: 1.4; | |||
color: #333; | |||
margin-bottom: 16rpx; | |||
display: -webkit-box; | |||
-webkit-box-orient: vertical; | |||
-webkit-line-clamp: 3; | |||
overflow: hidden; | |||
} | |||
.address { | |||
display: flex; | |||
align-items: center; | |||
margin-bottom: 20rpx; | |||
text { | |||
font-size: 24rpx; | |||
color: #666; | |||
margin-left: 8rpx; | |||
} | |||
} | |||
.user-info { | |||
display: flex; | |||
align-items: center; | |||
margin-bottom: 20rpx; | |||
.user-avatar { | |||
width: 60rpx; | |||
height: 60rpx; | |||
border-radius: 30rpx; | |||
overflow: hidden; | |||
margin-right: 16rpx; | |||
image { | |||
width: 100%; | |||
height: 100%; | |||
} | |||
} | |||
.user-details { | |||
flex: 1; | |||
.username { | |||
font-size: 26rpx; | |||
color: #333; | |||
font-weight: 500; | |||
margin-bottom: 6rpx; | |||
} | |||
.user-tags { | |||
display: flex; | |||
align-items: center; | |||
flex-wrap: wrap; | |||
.tag { | |||
font-size: 20rpx; | |||
color: white; | |||
background-color: #999; | |||
padding: 4rpx 12rpx; | |||
border-radius: 12rpx; | |||
margin-right: 8rpx; | |||
margin-bottom: 4rpx; | |||
} | |||
.auth-tag { | |||
font-size: 20rpx; | |||
color: white; | |||
background-color: #ffd036; | |||
padding: 4rpx 12rpx; | |||
border-radius: 12rpx; | |||
margin-right: 8rpx; | |||
margin-bottom: 4rpx; | |||
} | |||
} | |||
} | |||
} | |||
.interaction { | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
.interaction-item { | |||
display: flex; | |||
align-items: center; | |||
text { | |||
font-size: 24rpx; | |||
color: #999; | |||
margin-left: 6rpx; | |||
} | |||
} | |||
.publish-time { | |||
font-size: 24rpx; | |||
color: #999; | |||
margin-left: auto; | |||
} | |||
} | |||
} | |||
} | |||
</style> |
@ -0,0 +1,371 @@ | |||
<template> | |||
<view class="square-page"> | |||
<navbar title="广场"/> | |||
<!-- 一级分类:关注/发现 --> | |||
<view class="primary-tabs"> | |||
<view | |||
class="primary-tab-item" | |||
:class="{active: currentPrimaryTab === 0}" | |||
@click="switchPrimaryTab(0)" | |||
> | |||
关注 | |||
</view> | |||
<view | |||
class="primary-tab-item" | |||
:class="{active: currentPrimaryTab === 1}" | |||
@click="switchPrimaryTab(1)" | |||
> | |||
发现 | |||
</view> | |||
</view> | |||
<!-- 二级分类:城市列表 --> | |||
<!-- <view class="secondary-tabs"> | |||
<scroll-view scroll-x="true" class="city-scroll"> | |||
<view class="city-tabs"> | |||
<view | |||
class="city-tab-item" | |||
:class="{active: currentCityIndex === -1}" | |||
@click="switchCity(-1, null)" | |||
> | |||
全部 | |||
</view> | |||
<view | |||
v-for="(city, index) in cityList" | |||
:key="city.id || index" | |||
class="city-tab-item" | |||
:class="{active: currentCityIndex === index}" | |||
@click="switchCity(index, city)" | |||
> | |||
{{ city.name || city.cityName }} | |||
</view> | |||
</view> | |||
</scroll-view> | |||
</view> --> | |||
<!-- 使用uv-tabs组件的城市分类 --> | |||
<view class="city-tabs-container"> | |||
<uv-tabs | |||
:list="cityTabsList" | |||
:current="currentCityTabIndex" | |||
:activeStyle="{color: '#5baaff', fontWeight: 600}" | |||
lineColor="#5baaff" | |||
lineHeight="6rpx" | |||
lineWidth="40rpx" | |||
keyName="name" | |||
@click="onCityTabClick" | |||
/> | |||
</view> | |||
<!-- 瀑布流列表 --> | |||
<view class="content-container"> | |||
<waterfallContainer | |||
:list="List" | |||
@item-click="onItemClick" | |||
@item-like="onItemLike" | |||
/> | |||
<!-- 加载更多提示 --> | |||
<view v-if="loadmore && List.length > 0" class="load-more"> | |||
<uv-loading-icon size="28"></uv-loading-icon> | |||
<text class="load-more-text">加载更多...</text> | |||
</view> | |||
<!-- 没有更多数据提示 --> | |||
<view v-if="!loadmore && List.length > 0" class="no-more"> | |||
<text>— 没有更多了 —</text> | |||
</view> | |||
</view> | |||
<!-- 空状态 --> | |||
<view v-if="!loading && List.length === 0" class="empty-state"> | |||
<uv-empty | |||
text="暂无动态" | |||
textColor="#999" | |||
icon="list" | |||
iconColor="#ddd" | |||
iconSize="120" | |||
></uv-empty> | |||
</view> | |||
<!-- 加载状态 --> | |||
<view v-if="loading && List.length === 0" class="loading-state"> | |||
<uv-loading-icon size="40"></uv-loading-icon> | |||
<text class="loading-text">加载中...</text> | |||
</view> | |||
<tabber select="1" /> | |||
</view> | |||
</template> | |||
<script> | |||
import navbar from '@/components/base/navbar.vue' | |||
import tabber from '@/components/base/tabbar.vue' | |||
import waterfallContainer from '@/components/list/square/waterfallContainer.vue' | |||
import mixinsList from '@/mixins/loadList.js' | |||
import { mapState } from 'vuex' | |||
export default { | |||
mixins: [mixinsList], | |||
components: { | |||
navbar, | |||
tabber, | |||
waterfallContainer | |||
}, | |||
data() { | |||
return { | |||
mixinsListApi: 'getPostPage', // 使用与首页相同的API | |||
currentPrimaryTab: 1, // 默认显示发现 | |||
currentCityIndex: -1, // 默认全部城市 | |||
currentCity: null, | |||
currentCityTabIndex: 0 // uv-tabs当前选中的城市索引 | |||
} | |||
}, | |||
onLoad() { | |||
// 获取城市列表 | |||
this.$store.commit('getCityList') | |||
// 初始化查询参数 | |||
this.initQueryParams() | |||
}, | |||
onShow() { | |||
this.refreshList() | |||
}, | |||
onPullDownRefresh() { | |||
this.refreshList() | |||
}, | |||
onReachBottom() { | |||
this.loadMore() | |||
}, | |||
computed: { | |||
...mapState(['cityList']), | |||
// 城市标签页列表(包含"全部"选项) | |||
cityTabsList() { | |||
const allTab = { id: null, name: '全部' } | |||
const cityTabs = this.cityList.map(city => ({ | |||
id: city.id || city.cityId, | |||
name: city.name || city.cityName || city.title | |||
})) | |||
return [allTab, ...cityTabs] | |||
} | |||
}, | |||
methods: { | |||
// 初始化查询参数 | |||
initQueryParams() { | |||
// 设置默认查询参数 | |||
this.queryParams = { | |||
...this.queryParams, | |||
type: this.currentPrimaryTab // 0: 关注, 1: 发现 | |||
} | |||
// 如果有选择城市,添加城市参数 | |||
if (this.currentCity) { | |||
this.queryParams.cityId = this.currentCity.id || this.currentCity.cityId | |||
} | |||
}, | |||
// 切换一级分类 | |||
switchPrimaryTab(index) { | |||
if (this.currentPrimaryTab === index) return | |||
this.currentPrimaryTab = index | |||
this.queryParams.type = index | |||
this.refreshList() | |||
}, | |||
// 切换城市(已注释,改用uv-tabs的onCityTabClick方法) | |||
// switchCity(index, city) { | |||
// if (this.currentCityIndex === index) return | |||
// | |||
// this.currentCityIndex = index | |||
// this.currentCity = city | |||
// | |||
// // 更新查询参数 | |||
// if (city) { | |||
// this.queryParams.cityId = city.id || city.cityId | |||
// } else { | |||
// delete this.queryParams.cityId | |||
// } | |||
// | |||
// this.refreshList() | |||
// }, | |||
// 重写刷新方法 | |||
onRefresh() { | |||
this.refreshList() | |||
}, | |||
// 点击动态项 | |||
onItemClick(item) { | |||
this.$utils.navigateTo('/pages_order/post/postDetail?id=' + item.id) | |||
}, | |||
// 点赞动态 | |||
onItemLike(item) { | |||
console.log('点赞动态:', item.id) | |||
// 这里可以添加点赞API调用 | |||
}, | |||
// uv-tabs城市切换事件 | |||
onCityTabClick(item) { | |||
this.currentCityTabIndex = item.index | |||
// 如果是第一个(全部),清除城市筛选 | |||
if (item.index === 0) { | |||
this.currentCity = null | |||
this.currentCityIndex = -1 | |||
delete this.queryParams.cityId | |||
} else { | |||
// 获取对应的城市数据 | |||
const cityData = this.cityList[item.index - 1] // 减1是因为第一个是"全部" | |||
this.currentCity = cityData | |||
this.currentCityIndex = item.index - 1 | |||
this.queryParams.cityId = cityData.id || cityData.cityId | |||
} | |||
this.refreshList() | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang="scss"> | |||
.square-page { | |||
background-color: #f5f5f5; | |||
min-height: 100vh; | |||
} | |||
.primary-tabs { | |||
background-color: #fff; | |||
display: flex; | |||
padding: 0 30rpx; | |||
border-bottom: 1rpx solid #eee; | |||
.primary-tab-item { | |||
flex: 1; | |||
text-align: center; | |||
padding: 30rpx 0; | |||
font-size: 32rpx; | |||
color: #666; | |||
position: relative; | |||
&.active { | |||
color: #5baaff; | |||
font-weight: bold; | |||
&::after { | |||
content: ''; | |||
position: absolute; | |||
bottom: 0; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
width: 60rpx; | |||
height: 6rpx; | |||
background-color: #5baaff; | |||
border-radius: 3rpx; | |||
} | |||
} | |||
} | |||
} | |||
// 原有的二级分类样式(已注释) | |||
// .secondary-tabs { | |||
// background-color: #fff; | |||
// border-bottom: 1rpx solid #eee; | |||
// | |||
// .city-scroll { | |||
// padding: 20rpx 0; | |||
// | |||
// .city-tabs { | |||
// display: flex; | |||
// white-space: nowrap; | |||
// padding: 0 30rpx; | |||
// | |||
// .city-tab-item { | |||
// flex-shrink: 0; | |||
// padding: 16rpx 32rpx; | |||
// margin-right: 20rpx; | |||
// background-color: #f8f9fa; | |||
// border-radius: 30rpx; | |||
// font-size: 28rpx; | |||
// color: #666; | |||
// border: 1rpx solid transparent; | |||
// | |||
// &.active { | |||
// background-color: #5baaff; | |||
// color: #fff; | |||
// } | |||
// | |||
// &:last-child { | |||
// margin-right: 30rpx; | |||
// } | |||
// } | |||
// } | |||
// } | |||
// } | |||
// uv-tabs城市分类容器 | |||
.city-tabs-container { | |||
background-color: #fff; | |||
border-bottom: 1rpx solid #eee; | |||
padding: 0 20rpx; | |||
} | |||
.content-container { | |||
padding: 0; | |||
} | |||
.empty-state { | |||
padding: 100rpx 0; | |||
text-align: center; | |||
} | |||
.loading-state { | |||
padding: 100rpx 0; | |||
text-align: center; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
.loading-text { | |||
margin-top: 20rpx; | |||
font-size: 28rpx; | |||
color: #999; | |||
} | |||
} | |||
.load-more { | |||
padding: 30rpx 0; | |||
text-align: center; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
.load-more-text { | |||
margin-top: 15rpx; | |||
font-size: 24rpx; | |||
color: #999; | |||
} | |||
} | |||
.no-more { | |||
padding: 30rpx 0; | |||
text-align: center; | |||
text { | |||
font-size: 24rpx; | |||
color: #ccc; | |||
} | |||
} | |||
// 原有的城市滚动条隐藏样式(已注释,uv-tabs自带滚动处理) | |||
// /deep/ .city-scroll::-webkit-scrollbar { | |||
// display: none; | |||
// } | |||
</style> |
@ -0,0 +1,476 @@ | |||
<template> | |||
<view class="fans-list-page"> | |||
<navbar title="粉丝列表" leftClick @leftClick="$utils.navigateBack" /> | |||
<!-- 标签页切换 --> | |||
<view class="tabs-container"> | |||
<uv-tabs | |||
:list="tabsList" | |||
:current="currentTab" | |||
:activeStyle="{color: '#333', fontWeight: 600}" | |||
lineColor="#5baaff" | |||
lineHeight="6rpx" | |||
lineWidth="40rpx" | |||
keyName="name" | |||
@click="onTabClick" | |||
/> | |||
</view> | |||
<!-- 用户列表 --> | |||
<view class="user-list"> | |||
<view | |||
class="user-item" | |||
v-for="(user, index) in userList" | |||
:key="index" | |||
@click="goToUserProfile(user)" | |||
> | |||
<view class="user-avatar"> | |||
<image :src="user.headImage" mode="aspectFill"></image> | |||
</view> | |||
<view class="user-info"> | |||
<view class="user-name">{{ user.nickName }}</view> | |||
<view class="user-desc"> | |||
<view class="user-tags"> | |||
<text class="tag" v-if="user.sex">{{ user.sex }}</text> | |||
<text class="tag" v-if="user.address">{{ user.address }}</text> | |||
<text class="auth-tag" v-if="user.idCardOpen">{{ getAuthText(user.idCardOpen) }}</text> | |||
</view> | |||
</view> | |||
</view> | |||
<view class="user-action" v-if="!isCurrentUserPage"> | |||
<button | |||
class="follow-btn" | |||
:class="{followed: user.isFollowed}" | |||
@click.stop="toggleFollow(user, index)" | |||
> | |||
{{ user.isFollowed ? '已关注' : '关注' }} | |||
</button> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 空状态 --> | |||
<view v-if="!loading && userList.length === 0" class="empty-state"> | |||
<uv-empty | |||
:text="emptyText" | |||
icon="account" | |||
iconSize="120" | |||
></uv-empty> | |||
</view> | |||
<!-- 加载状态 --> | |||
<view v-if="loading" class="loading-state"> | |||
<uv-loading-icon size="40"></uv-loading-icon> | |||
<text>加载中...</text> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
import { mapState } from 'vuex' | |||
export default { | |||
data() { | |||
return { | |||
userId: '', // 用户ID | |||
type: 'fans', // fans: 粉丝, following: 关注 | |||
currentTab: 0, | |||
tabsList: [ | |||
{ name: '粉丝', type: 'fans' }, | |||
{ name: '关注', type: 'following' } | |||
], | |||
userList: [], // 用户列表 | |||
loading: false, | |||
// 模拟数据 | |||
mockFansData: [ | |||
{ | |||
id: '1', | |||
nickName: '小美同学', | |||
headImage: 'https://picsum.photos/100/100?random=1', | |||
sex: '女', | |||
address: '北京', | |||
idCardOpen: 1, | |||
isFollowed: false | |||
}, | |||
{ | |||
id: '2', | |||
nickName: '程序员小王', | |||
headImage: 'https://picsum.photos/100/100?random=2', | |||
sex: '男', | |||
address: '上海', | |||
idCardOpen: 2, | |||
isFollowed: true | |||
}, | |||
{ | |||
id: '3', | |||
nickName: '设计师小李', | |||
headImage: 'https://picsum.photos/100/100?random=3', | |||
sex: '女', | |||
address: '深圳', | |||
idCardOpen: 0, | |||
isFollowed: false | |||
}, | |||
{ | |||
id: '4', | |||
nickName: '产品经理老张', | |||
headImage: 'https://picsum.photos/100/100?random=4', | |||
sex: '男', | |||
address: '广州', | |||
idCardOpen: 1, | |||
isFollowed: true | |||
}, | |||
{ | |||
id: '5', | |||
nickName: '运营小姐姐', | |||
headImage: 'https://picsum.photos/100/100?random=5', | |||
sex: '女', | |||
address: '杭州', | |||
idCardOpen: 2, | |||
isFollowed: false | |||
}, | |||
{ | |||
id: '6', | |||
nickName: '前端工程师', | |||
headImage: 'https://picsum.photos/100/100?random=6', | |||
sex: '男', | |||
address: '成都', | |||
idCardOpen: 1, | |||
isFollowed: true | |||
}, | |||
{ | |||
id: '7', | |||
nickName: 'UI设计师', | |||
headImage: 'https://picsum.photos/100/100?random=7', | |||
sex: '女', | |||
address: '武汉', | |||
idCardOpen: 0, | |||
isFollowed: false | |||
}, | |||
{ | |||
id: '8', | |||
nickName: '后端大神', | |||
headImage: 'https://picsum.photos/100/100?random=8', | |||
sex: '男', | |||
address: '西安', | |||
idCardOpen: 2, | |||
isFollowed: true | |||
} | |||
], | |||
mockFollowingData: [ | |||
{ | |||
id: '9', | |||
nickName: '技术大牛', | |||
headImage: 'https://picsum.photos/100/100?random=9', | |||
sex: '男', | |||
address: '北京', | |||
idCardOpen: 2, | |||
isFollowed: true | |||
}, | |||
{ | |||
id: '10', | |||
nickName: '设计总监', | |||
headImage: 'https://picsum.photos/100/100?random=10', | |||
sex: '女', | |||
address: '上海', | |||
idCardOpen: 2, | |||
isFollowed: true | |||
}, | |||
{ | |||
id: '11', | |||
nickName: '产品总监', | |||
headImage: 'https://picsum.photos/100/100?random=11', | |||
sex: '男', | |||
address: '深圳', | |||
idCardOpen: 1, | |||
isFollowed: true | |||
}, | |||
{ | |||
id: '12', | |||
nickName: '运营总监', | |||
headImage: 'https://picsum.photos/100/100?random=12', | |||
sex: '女', | |||
address: '广州', | |||
idCardOpen: 2, | |||
isFollowed: true | |||
}, | |||
{ | |||
id: '13', | |||
nickName: '架构师', | |||
headImage: 'https://picsum.photos/100/100?random=13', | |||
sex: '男', | |||
address: '杭州', | |||
idCardOpen: 1, | |||
isFollowed: true | |||
}, | |||
{ | |||
id: '14', | |||
nickName: '资深设计师', | |||
headImage: 'https://picsum.photos/100/100?random=14', | |||
sex: '女', | |||
address: '成都', | |||
idCardOpen: 2, | |||
isFollowed: true | |||
} | |||
] | |||
} | |||
}, | |||
computed: { | |||
...mapState(['userInfo']), | |||
// 页面标题 | |||
pageTitle() { | |||
return this.type === 'fans' ? '粉丝列表' : '关注列表' | |||
}, | |||
// 空状态文本 | |||
emptyText() { | |||
return this.type === 'fans' ? '暂无粉丝' : '暂无关注' | |||
}, | |||
// 是否是当前用户的页面 | |||
isCurrentUserPage() { | |||
return this.userId === this.userInfo.id | |||
} | |||
}, | |||
onLoad(options) { | |||
this.userId = options.userId || this.userInfo.id | |||
this.type = options.type || 'fans' | |||
// 设置初始tab | |||
this.currentTab = this.type === 'fans' ? 0 : 1 | |||
this.loadUserList() | |||
}, | |||
onPullDownRefresh() { | |||
this.loadUserList() | |||
}, | |||
methods: { | |||
// 返回上一页 | |||
goBack() { | |||
uni.navigateBack() | |||
}, | |||
// 标签页切换 | |||
onTabClick(item) { | |||
this.currentTab = item.index | |||
this.type = item.type | |||
this.loadUserList() | |||
}, | |||
// 获取认证文本 | |||
getAuthText(status) { | |||
const authTexts = ['审核中', '个人认证', '店铺认证'] | |||
return authTexts[status] || '未认证' | |||
}, | |||
// 跳转到用户主页 | |||
goToUserProfile(user) { | |||
uni.navigateTo({ | |||
url: `/pages_order/profile/userProfile?userId=${user.id}` | |||
}) | |||
}, | |||
// 切换关注状态 | |||
toggleFollow(user, index) { | |||
if (!uni.getStorageSync('token')) { | |||
uni.showToast({ | |||
title: '请先登录', | |||
icon: 'none' | |||
}) | |||
return | |||
} | |||
const isFollowed = !user.isFollowed | |||
// 模拟API调用 | |||
setTimeout(() => { | |||
// 更新本地数据 | |||
this.userList[index].isFollowed = isFollowed | |||
// 同时更新模拟数据源 | |||
if (this.type === 'fans') { | |||
const mockIndex = this.mockFansData.findIndex(item => item.id === user.id) | |||
if (mockIndex !== -1) { | |||
this.mockFansData[mockIndex].isFollowed = isFollowed | |||
} | |||
} else { | |||
const mockIndex = this.mockFollowingData.findIndex(item => item.id === user.id) | |||
if (mockIndex !== -1) { | |||
this.mockFollowingData[mockIndex].isFollowed = isFollowed | |||
} | |||
} | |||
uni.showToast({ | |||
title: isFollowed ? '关注成功' : '取消关注', | |||
icon: 'success' | |||
}) | |||
}, 500) | |||
}, | |||
// 加载用户列表 | |||
loadUserList() { | |||
this.loading = true | |||
// 模拟网络延迟 | |||
setTimeout(() => { | |||
this.loading = false | |||
uni.stopPullDownRefresh() | |||
// 使用模拟数据 | |||
if (this.type === 'fans') { | |||
this.userList = [...this.mockFansData] | |||
} else { | |||
this.userList = [...this.mockFollowingData] | |||
} | |||
// 模拟API调用成功 | |||
console.log(`加载${this.type === 'fans' ? '粉丝' : '关注'}列表成功`) | |||
}, 1000) | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang="scss"> | |||
.fans-list-page { | |||
background-color: #f5f5f5; | |||
min-height: 100vh; | |||
} | |||
.navbar { | |||
position: fixed; | |||
top: 0; | |||
left: 0; | |||
right: 0; | |||
z-index: 100; | |||
height: 88rpx; | |||
background: #fff; | |||
border-bottom: 1rpx solid #eee; | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
padding: 0 30rpx; | |||
padding-top: var(--status-bar-height, 44rpx); | |||
.nav-title { | |||
color: #333; | |||
font-size: 32rpx; | |||
font-weight: 600; | |||
} | |||
.nav-left, .nav-right { | |||
width: 60rpx; | |||
height: 60rpx; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
} | |||
} | |||
.tabs-container { | |||
margin-top: 88rpx; | |||
padding-top: var(--status-bar-height, 44rpx); | |||
background: #fff; | |||
border-bottom: 1rpx solid #eee; | |||
padding-left: 20rpx; | |||
padding-right: 20rpx; | |||
} | |||
.user-list { | |||
padding: 20rpx; | |||
.user-item { | |||
display: flex; | |||
align-items: center; | |||
background: #fff; | |||
padding: 30rpx; | |||
margin-bottom: 20rpx; | |||
border-radius: 16rpx; | |||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); | |||
.user-avatar { | |||
width: 100rpx; | |||
height: 100rpx; | |||
border-radius: 50rpx; | |||
overflow: hidden; | |||
margin-right: 30rpx; | |||
image { | |||
width: 100%; | |||
height: 100%; | |||
} | |||
} | |||
.user-info { | |||
flex: 1; | |||
.user-name { | |||
font-size: 32rpx; | |||
font-weight: 600; | |||
color: #333; | |||
margin-bottom: 12rpx; | |||
} | |||
.user-desc { | |||
.user-tags { | |||
display: flex; | |||
flex-wrap: wrap; | |||
gap: 8rpx; | |||
.tag { | |||
background: #f0f0f0; | |||
color: #666; | |||
padding: 4rpx 12rpx; | |||
border-radius: 12rpx; | |||
font-size: 20rpx; | |||
} | |||
.auth-tag { | |||
background: #52c41a; | |||
color: #fff; | |||
padding: 4rpx 12rpx; | |||
border-radius: 12rpx; | |||
font-size: 20rpx; | |||
} | |||
} | |||
} | |||
} | |||
.user-action { | |||
.follow-btn { | |||
background: #5baaff; | |||
color: #fff; | |||
border: none; | |||
padding: 16rpx 32rpx; | |||
border-radius: 30rpx; | |||
font-size: 24rpx; | |||
&.followed { | |||
background: #f0f0f0; | |||
color: #666; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.empty-state { | |||
padding: 100rpx 0; | |||
text-align: center; | |||
} | |||
.loading-state { | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
padding: 60rpx 0; | |||
text { | |||
margin-top: 20rpx; | |||
font-size: 28rpx; | |||
color: #999; | |||
} | |||
} | |||
</style> |
@ -0,0 +1,797 @@ | |||
<template> | |||
<view class="profile-page"> | |||
<!-- 顶部导航 --> | |||
<!-- <navbar | |||
:title="userProfile.nickName || '用户主页'" | |||
:leftClick="true" | |||
:moreClick="showMoreOptions" | |||
bgColor="transparent" | |||
color="#fff" | |||
@leftClick="$utils.navigateBack" | |||
/> --> | |||
<!-- 用户信息头部 --> | |||
<view class="profile-header"> | |||
<!-- 背景装饰 --> | |||
<view class="header-bg"></view> | |||
<!-- 返回按钮 --> | |||
<view class="back-btn" @click="$utils.navigateBack"> | |||
<uv-icon name="arrow-left" size="30rpx" color="#fff"></uv-icon> | |||
</view> | |||
<!-- 用户基本信息 --> | |||
<view class="user-info"> | |||
<view class="user-avatar"> | |||
<image :src="userProfile.headImage" mode="aspectFill" @click="previewAvatar"></image> | |||
<!-- VIP标识 --> | |||
<view class="vip-badge" v-if="userProfile.isPay"> | |||
<text>{{ getVipLevel(userProfile.isPay) }}</text> | |||
</view> | |||
</view> | |||
<view class="user-details"> | |||
<view class="username">{{ userProfile.nickName }}</view> | |||
<view class="user-tags"> | |||
<!-- 性别年龄 --> | |||
<view class="tag gender-tag" v-if="userProfile.sex"> | |||
<uv-icon :name="sexIcons[userProfile.sex]" size="24rpx" :color="sexColors[userProfile.sex]"></uv-icon> | |||
<text>{{ userProfile.sex }}</text> | |||
<text v-if="userProfile.yearDate">{{ getAge() }}岁</text> | |||
</view> | |||
<!-- 地址 --> | |||
<view class="tag location-tag" v-if="userProfile.address"> | |||
<uv-icon name="map-pin" size="20rpx" color="#666"></uv-icon> | |||
<text>{{ userProfile.address }}</text> | |||
</view> | |||
<!-- 认证状态 --> | |||
<view class="tag auth-tag" v-if="userProfile.idCardOpen"> | |||
<uv-icon name="checkmark-circle-fill" size="20rpx" color="#52c41a"></uv-icon> | |||
<text>{{ getAuthText(userProfile.idCardOpen) }}</text> | |||
</view> | |||
</view> | |||
<!-- 学校信息 --> | |||
<view class="school-info" v-if="userProfile.czSchool || userProfile.gzSchool"> | |||
<text v-if="userProfile.czSchool">🎓 {{ userProfile.czSchool }}</text> | |||
<text v-if="userProfile.gzSchool">🏫 {{ userProfile.gzSchool }}</text> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 数据统计 --> | |||
<view class="stats-row"> | |||
<view class="stat-item" @click="showFans"> | |||
<view class="stat-number">{{ userProfile.intentionNum || 0 }}</view> | |||
<view class="stat-label">粉丝</view> | |||
</view> | |||
<view class="stat-item" @click="showFollowing"> | |||
<view class="stat-number">{{ userProfile.followNum || 0 }}</view> | |||
<view class="stat-label">关注</view> | |||
</view> | |||
<view class="stat-item" @click="showLikes"> | |||
<view class="stat-number">{{ userProfile.likeNum || 0 }}</view> | |||
<view class="stat-label">获赞</view> | |||
</view> | |||
</view> | |||
<!-- 操作按钮 --> | |||
<view class="action-buttons" v-if="!isCurrentUser"> | |||
<button class="follow-btn" :class="{followed: isFollowed}" @click="toggleFollow"> | |||
<uv-icon :name="isFollowed ? 'checkmark' : 'plus'" size="24rpx"></uv-icon> | |||
<text>{{ isFollowed ? '已关注' : '关注' }}</text> | |||
</button> | |||
<button class="message-btn" @click="sendMessage"> | |||
<uv-icon name="chat" size="24rpx"></uv-icon> | |||
<text>私信</text> | |||
</button> | |||
</view> | |||
</view> | |||
<!-- 内容标签页 --> | |||
<view class="content-tabs"> | |||
<uv-tabs | |||
:list="contentTabs" | |||
:current="currentTabIndex" | |||
:activeStyle="{color: '#333', fontWeight: 600}" | |||
lineColor="#5baaff" | |||
lineHeight="6rpx" | |||
lineWidth="40rpx" | |||
keyName="name" | |||
@click="onTabClick" | |||
/> | |||
</view> | |||
<!-- 内容区域 --> | |||
<view class="content-container"> | |||
<!-- 帖子 - 瀑布流展示 --> | |||
<view v-if="currentTabIndex === 0" class="posts-content"> | |||
<waterfallContainer | |||
v-if="postsList.length > 0" | |||
:list="postsList" | |||
@item-click="onPostClick" | |||
@item-like="onPostLike" | |||
/> | |||
</view> | |||
<!-- 租房信息 --> | |||
<view v-else-if="currentTabIndex === 1" class="renting-content"> | |||
<rentingItem | |||
v-for="(item, index) in rentingList" | |||
:key="index" | |||
:item="item" | |||
@click="onRentingClick(item)" | |||
/> | |||
</view> | |||
<!-- 招聘信息 --> | |||
<view v-else-if="currentTabIndex === 2" class="work-content"> | |||
<workItem | |||
v-for="(item, index) in workList" | |||
:key="index" | |||
:item="item" | |||
@click="onWorkClick(item)" | |||
/> | |||
</view> | |||
<!-- 店铺信息 --> | |||
<view v-else-if="currentTabIndex === 3" class="shop-content"> | |||
<gourmetItem | |||
v-for="(item, index) in shopList" | |||
:key="index" | |||
:item="item" | |||
@click="onShopClick(item)" | |||
/> | |||
</view> | |||
<!-- 加载更多提示 --> | |||
<view v-if="showLoadMore" class="load-more-state"> | |||
<uv-loading-icon size="32"></uv-loading-icon> | |||
<text>加载更多...</text> | |||
</view> | |||
<!-- 没有更多数据提示 --> | |||
<view v-if="showNoMore" class="no-more-state"> | |||
<text>— 没有更多了 —</text> | |||
</view> | |||
</view> | |||
<!-- 空状态 --> | |||
<view v-if="showEmptyState" class="empty-state"> | |||
<uv-empty | |||
:text="getEmptyText()" | |||
:icon="getEmptyIcon()" | |||
iconSize="120" | |||
></uv-empty> | |||
</view> | |||
<!-- 初始加载状态 --> | |||
<view v-if="loading && currentList.length === 0" class="loading-state"> | |||
<uv-loading-icon size="40"></uv-loading-icon> | |||
<text>加载中...</text> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
import navbar from '@/components/base/navbar.vue' | |||
import waterfallContainer from '@/components/list/square/waterfallContainer.vue' | |||
import rentingItem from '@/components/list/renting/rentingItem.vue' | |||
import workItem from '@/components/list/work/workItem.vue' | |||
import gourmetItem from '@/components/list/gourmet/gourmetItem.vue' | |||
import { mapState } from 'vuex' | |||
export default { | |||
components: { | |||
navbar, | |||
waterfallContainer, | |||
rentingItem, | |||
workItem, | |||
gourmetItem | |||
}, | |||
data() { | |||
return { | |||
userId: '', // 要查看的用户ID | |||
userProfile: {}, // 用户资料 | |||
currentTabIndex: 0, // 当前标签页索引 | |||
contentTabs: [ | |||
{ name: '帖子', icon: 'grid' }, | |||
{ name: '租房', icon: 'home' }, | |||
{ name: '招聘', icon: 'search' }, | |||
{ name: '店铺', icon: 'shop' } | |||
], | |||
postsList: [], // 帖子列表 | |||
rentingList: [], // 租房列表 | |||
workList: [], // 招聘列表 | |||
shopList: [], // 店铺列表 | |||
loading: false, // 初始加载状态 | |||
loadingMore: false, // 加载更多状态 | |||
isFollowed: false, // 是否已关注 | |||
// 分页参数 | |||
pageParams: { | |||
postsList: { pageNum: 1, hasMore: true }, | |||
rentingList: { pageNum: 1, hasMore: true }, | |||
workList: { pageNum: 1, hasMore: true }, | |||
shopList: { pageNum: 1, hasMore: true } | |||
}, | |||
sexIcons: { | |||
'男': 'mars', | |||
'女': 'venus', | |||
'其他': 'transgender' | |||
}, | |||
sexColors: { | |||
'男': '#4A90E2', | |||
'女': '#FF69B4', | |||
'其他': '#999' | |||
} | |||
} | |||
}, | |||
computed: { | |||
...mapState(['userInfo']), | |||
// 是否是当前登录用户 | |||
isCurrentUser() { | |||
return this.userInfo.id === this.userId | |||
}, | |||
// 当前显示的列表 | |||
currentList() { | |||
const listMap = ['postsList', 'rentingList', 'workList', 'shopList'] | |||
return this[listMap[this.currentTabIndex]] || [] | |||
}, | |||
// 当前列表的分页参数 | |||
currentPageParams() { | |||
const listMap = ['postsList', 'rentingList', 'workList', 'shopList'] | |||
return this.pageParams[listMap[this.currentTabIndex]] | |||
}, | |||
// 是否显示空状态 | |||
showEmptyState() { | |||
return !this.loading && this.currentList.length === 0 | |||
}, | |||
// 是否显示加载更多 | |||
showLoadMore() { | |||
return this.currentList.length > 0 && this.currentPageParams.hasMore && this.loadingMore | |||
}, | |||
// 是否显示没有更多 | |||
showNoMore() { | |||
return this.currentList.length > 0 && !this.currentPageParams.hasMore | |||
} | |||
}, | |||
onLoad(options) { | |||
this.userId = options.userId || this.userInfo.id | |||
this.loadUserProfile() | |||
this.loadUserContent(false) | |||
}, | |||
onPullDownRefresh() { | |||
this.loadUserProfile() | |||
this.refreshUserContent() | |||
}, | |||
onReachBottom() { | |||
this.loadMoreContent() | |||
}, | |||
methods: { | |||
// 显示更多选项 | |||
showMoreOptions() { | |||
uni.showActionSheet({ | |||
itemList: ['举报用户', '拉黑用户'], | |||
success: (res) => { | |||
if (res.tapIndex === 0) { | |||
this.reportUser() | |||
} else if (res.tapIndex === 1) { | |||
this.blockUser() | |||
} | |||
} | |||
}) | |||
}, | |||
// 预览头像 | |||
previewAvatar() { | |||
if (this.userProfile.headImage) { | |||
uni.previewImage({ | |||
urls: [this.userProfile.headImage] | |||
}) | |||
} | |||
}, | |||
// 获取VIP等级 | |||
getVipLevel(level) { | |||
const levels = ['', 'VIP', 'SVIP'] | |||
return levels[level] || '' | |||
}, | |||
// 获取年龄 | |||
getAge() { | |||
if (!this.userProfile.yearDate) return '' | |||
const birthYear = parseInt(this.userProfile.yearDate) | |||
const currentYear = new Date().getFullYear() | |||
return currentYear - birthYear | |||
}, | |||
// 获取认证文本 | |||
getAuthText(status) { | |||
const authTexts = ['审核中', '个人认证', '店铺认证'] | |||
return authTexts[status] || '未认证' | |||
}, | |||
// 标签页切换 | |||
onTabClick(item) { | |||
this.currentTabIndex = item.index | |||
this.refreshUserContent() | |||
}, | |||
// 切换关注状态 | |||
toggleFollow() { | |||
if (!uni.getStorageSync('token')) { | |||
uni.showToast({ | |||
title: '请先登录', | |||
icon: 'none' | |||
}) | |||
return | |||
} | |||
this.isFollowed = !this.isFollowed | |||
// 这里调用关注/取消关注API | |||
this.$api(this.isFollowed ? 'followUser' : 'unfollowUser', { | |||
userId: this.userId | |||
}, res => { | |||
if (res.code === 200) { | |||
uni.showToast({ | |||
title: this.isFollowed ? '关注成功' : '取消关注', | |||
icon: 'success' | |||
}) | |||
} | |||
}) | |||
}, | |||
// 发送私信 | |||
sendMessage() { | |||
if (!uni.getStorageSync('token')) { | |||
uni.showToast({ | |||
title: '请先登录', | |||
icon: 'none' | |||
}) | |||
return | |||
} | |||
uni.navigateTo({ | |||
url: `/pages_order/chat/chatDetail?userId=${this.userId}` | |||
}) | |||
}, | |||
// 显示粉丝列表 | |||
showFans() { | |||
uni.navigateTo({ | |||
url: `/pages_order/profile/fansList?userId=${this.userId}&type=fans` | |||
}) | |||
}, | |||
// 显示关注列表 | |||
showFollowing() { | |||
uni.navigateTo({ | |||
url: `/pages_order/profile/fansList?userId=${this.userId}&type=following` | |||
}) | |||
}, | |||
// 显示点赞列表 | |||
showLikes() { | |||
uni.showToast({ | |||
title: '功能开发中', | |||
icon: 'none' | |||
}) | |||
}, | |||
// 加载用户资料 | |||
loadUserProfile() { | |||
this.loading = true | |||
this.$api('getUserProfile', { userId: this.userId }, res => { | |||
this.loading = false | |||
uni.stopPullDownRefresh() | |||
if (res.code === 200) { | |||
this.userProfile = res.result | |||
// 检查是否已关注(如果不是当前用户) | |||
if (!this.isCurrentUser) { | |||
this.checkFollowStatus() | |||
} | |||
} | |||
}) | |||
}, | |||
// 检查关注状态 | |||
checkFollowStatus() { | |||
this.$api('checkFollowStatus', { userId: this.userId }, res => { | |||
if (res.code === 200) { | |||
this.isFollowed = res.result.isFollowed | |||
} | |||
}) | |||
}, | |||
// 刷新用户内容(重置分页) | |||
refreshUserContent() { | |||
const listMap = ['postsList', 'rentingList', 'workList', 'shopList'] | |||
const currentListKey = listMap[this.currentTabIndex] | |||
// 重置分页参数 | |||
this.pageParams[currentListKey].pageNum = 1 | |||
this.pageParams[currentListKey].hasMore = true | |||
// 清空当前列表 | |||
this[currentListKey] = [] | |||
// 加载第一页数据 | |||
this.loadUserContent(false) | |||
}, | |||
// 加载用户内容 | |||
loadUserContent(isLoadMore = false) { | |||
const apiMap = [ | |||
'getUserPosts', // 帖子 | |||
'getUserRenting', // 租房 | |||
'getUserWork', // 招聘 | |||
'getUserShop' // 店铺 | |||
] | |||
const listMap = ['postsList', 'rentingList', 'workList', 'shopList'] | |||
const currentListKey = listMap[this.currentTabIndex] | |||
const currentPageParams = this.pageParams[currentListKey] | |||
// 设置加载状态 | |||
if (isLoadMore) { | |||
this.loadingMore = true | |||
} else { | |||
this.loading = true | |||
} | |||
this.$api(apiMap[this.currentTabIndex], { | |||
userId: this.userId, | |||
pageNum: currentPageParams.pageNum, | |||
pageSize: 20 | |||
}, res => { | |||
// 清除加载状态 | |||
this.loading = false | |||
this.loadingMore = false | |||
uni.stopPullDownRefresh() | |||
if (res.code === 200) { | |||
const newData = res.result.records || res.result || [] | |||
if (isLoadMore) { | |||
// 加载更多:追加数据 | |||
this[currentListKey] = [...this[currentListKey], ...newData] | |||
} else { | |||
// 首次加载:替换数据 | |||
this[currentListKey] = newData | |||
} | |||
// 更新分页状态 | |||
if (newData.length < 20) { | |||
// 数据不足一页,说明没有更多了 | |||
this.pageParams[currentListKey].hasMore = false | |||
} else { | |||
// 还有更多数据,页码+1 | |||
this.pageParams[currentListKey].pageNum += 1 | |||
} | |||
} else { | |||
// 请求失败,恢复分页参数 | |||
if (isLoadMore && currentPageParams.pageNum > 1) { | |||
this.pageParams[currentListKey].pageNum -= 1 | |||
} | |||
} | |||
}) | |||
}, | |||
// 加载更多内容 | |||
loadMoreContent() { | |||
// 检查是否有更多数据和是否正在加载 | |||
if (!this.currentPageParams.hasMore || this.loadingMore || this.loading) { | |||
return | |||
} | |||
// 检查当前列表是否有数据 | |||
if (this.currentList.length === 0) { | |||
return | |||
} | |||
this.loadUserContent(true) | |||
}, | |||
// 点击帖子 | |||
onPostClick(item) { | |||
this.$utils.navigateTo(`/pages_order/post/postDetail?id=${item.id}`) | |||
}, | |||
// 点赞帖子 | |||
onPostLike(item) { | |||
console.log('点赞帖子:', item.id) | |||
}, | |||
// 点击租房 | |||
onRentingClick(item) { | |||
this.$utils.navigateTo(`/pages_order/renting/rentingDetail?id=${item.id}`) | |||
}, | |||
// 点击招聘 | |||
onWorkClick(item) { | |||
this.$utils.navigateTo(`/pages_order/work/workDetail?id=${item.id}`) | |||
}, | |||
// 点击店铺 | |||
onShopClick(item) { | |||
this.$utils.navigateTo(`/pages_order/gourmet/gourmetDetail?id=${item.id}`) | |||
}, | |||
// 举报用户 | |||
reportUser() { | |||
uni.showToast({ | |||
title: '举报功能开发中', | |||
icon: 'none' | |||
}) | |||
}, | |||
// 拉黑用户 | |||
blockUser() { | |||
uni.showModal({ | |||
title: '确认拉黑该用户?', | |||
success: (res) => { | |||
if (res.confirm) { | |||
// 调用拉黑API | |||
uni.showToast({ | |||
title: '拉黑功能开发中', | |||
icon: 'none' | |||
}) | |||
} | |||
} | |||
}) | |||
}, | |||
// 获取空状态文本 | |||
getEmptyText() { | |||
const emptyTexts = ['暂无帖子', '暂无租房信息', '暂无招聘信息', '暂无店铺信息'] | |||
return emptyTexts[this.currentTabIndex] || '暂无数据' | |||
}, | |||
// 获取空状态图标 | |||
getEmptyIcon() { | |||
const emptyIcons = ['list', 'home', 'search', 'shop'] | |||
return emptyIcons[this.currentTabIndex] || 'list' | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang="scss"> | |||
.profile-page { | |||
background-color: #f5f5f5; | |||
min-height: 100vh; | |||
} | |||
.profile-header { | |||
position: relative; | |||
padding: 40rpx 30rpx 40rpx; | |||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |||
padding-top: 180rpx; | |||
.header-bg { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
right: 0; | |||
bottom: 0; | |||
background: linear-gradient(135deg, rgba(102, 126, 234, 0.9) 0%, rgba(118, 75, 162, 0.9) 100%); | |||
} | |||
.back-btn { | |||
position: absolute; | |||
top: 80rpx; | |||
left: 30rpx; | |||
z-index: 3; | |||
width: 70rpx; | |||
height: 70rpx; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
background: rgba(255, 255, 255, 0.2); | |||
border-radius: 50%; | |||
backdrop-filter: blur(10rpx); | |||
} | |||
.user-info { | |||
position: relative; | |||
z-index: 2; | |||
display: flex; | |||
margin-bottom: 40rpx; | |||
.user-avatar { | |||
position: relative; | |||
margin-right: 30rpx; | |||
image { | |||
width: 160rpx; | |||
height: 160rpx; | |||
border-radius: 80rpx; | |||
border: 6rpx solid rgba(255, 255, 255, 0.3); | |||
} | |||
.vip-badge { | |||
position: absolute; | |||
bottom: -10rpx; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
background: linear-gradient(45deg, #FFD700, #FFA500); | |||
color: #333; | |||
padding: 6rpx 16rpx; | |||
border-radius: 20rpx; | |||
font-size: 20rpx; | |||
font-weight: 600; | |||
} | |||
} | |||
.user-details { | |||
flex: 1; | |||
color: #fff; | |||
.username { | |||
font-size: 36rpx; | |||
font-weight: 600; | |||
margin-bottom: 16rpx; | |||
} | |||
.user-tags { | |||
display: flex; | |||
flex-wrap: wrap; | |||
gap: 12rpx; | |||
margin-bottom: 16rpx; | |||
.tag { | |||
display: flex; | |||
align-items: center; | |||
background: rgba(255, 255, 255, 0.2); | |||
padding: 8rpx 16rpx; | |||
border-radius: 20rpx; | |||
font-size: 22rpx; | |||
text { | |||
margin-left: 6rpx; | |||
} | |||
} | |||
} | |||
.school-info { | |||
font-size: 24rpx; | |||
opacity: 0.9; | |||
text { | |||
display: block; | |||
margin-bottom: 6rpx; | |||
} | |||
} | |||
} | |||
} | |||
.stats-row { | |||
position: relative; | |||
z-index: 2; | |||
display: flex; | |||
justify-content: space-around; | |||
margin-bottom: 40rpx; | |||
.stat-item { | |||
text-align: center; | |||
color: #fff; | |||
.stat-number { | |||
font-size: 40rpx; | |||
font-weight: 600; | |||
margin-bottom: 8rpx; | |||
} | |||
.stat-label { | |||
font-size: 24rpx; | |||
opacity: 0.8; | |||
} | |||
} | |||
} | |||
.action-buttons { | |||
position: relative; | |||
z-index: 2; | |||
display: flex; | |||
gap: 20rpx; | |||
button { | |||
flex: 1; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
padding: 20rpx 0; | |||
border-radius: 50rpx; | |||
font-size: 28rpx; | |||
border: none; | |||
text { | |||
margin-left: 8rpx; | |||
} | |||
} | |||
.follow-btn { | |||
background: #fff; | |||
color: #333; | |||
&.followed { | |||
background: rgba(255, 255, 255, 0.3); | |||
color: #fff; | |||
} | |||
} | |||
.message-btn { | |||
background: rgba(255, 255, 255, 0.3); | |||
color: #fff; | |||
} | |||
} | |||
} | |||
.content-tabs { | |||
background: #fff; | |||
border-bottom: 1rpx solid #eee; | |||
padding: 0 20rpx; | |||
} | |||
.content-container { | |||
min-height: 400rpx; | |||
.posts-content, | |||
.renting-content, | |||
.work-content, | |||
.shop-content { | |||
padding: 20rpx 0; | |||
} | |||
} | |||
.empty-state { | |||
padding: 100rpx 0; | |||
text-align: center; | |||
} | |||
.loading-state { | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
padding: 60rpx 0; | |||
text { | |||
margin-top: 20rpx; | |||
font-size: 28rpx; | |||
color: #999; | |||
} | |||
} | |||
.load-more-state { | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
padding: 40rpx 0; | |||
text { | |||
margin-top: 15rpx; | |||
font-size: 24rpx; | |||
color: #666; | |||
} | |||
} | |||
.no-more-state { | |||
padding: 30rpx 0; | |||
text-align: center; | |||
text { | |||
font-size: 24rpx; | |||
color: #ccc; | |||
} | |||
} | |||
</style> |