upload project
This commit is contained in:
commit
06961cae04
422 changed files with 110626 additions and 0 deletions
436
pages/user-profile/user-profile.js
Normal file
436
pages/user-profile/user-profile.js
Normal file
|
|
@ -0,0 +1,436 @@
|
|||
const app = getApp();
|
||||
const config = require('../../config/config.js');
|
||||
const apiClient = require('../../utils/api-client.js');
|
||||
const imageCacheManager = require('../../utils/image-cache-manager.js');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// 用户基本信息
|
||||
userInfo: null,
|
||||
// 动态列表
|
||||
feedList: [],
|
||||
// 统计数据
|
||||
stats: {
|
||||
friendsCount: 0,
|
||||
postsCount: 0,
|
||||
likesCount: 0
|
||||
},
|
||||
// 加载状态
|
||||
loading: true,
|
||||
error: '',
|
||||
// 页面相关
|
||||
currentPage: 1,
|
||||
pageSize: 5,
|
||||
hasMoreData: true,
|
||||
loadingMore: false,
|
||||
// 当前年份
|
||||
currentYear: new Date().getFullYear(),
|
||||
// 是否是当前用户的资料页
|
||||
isCurrentUser: false,
|
||||
// 关注状态
|
||||
isFollowing: false,
|
||||
// 好友状态
|
||||
isFriend: false,
|
||||
// 系统适配信息
|
||||
systemInfo: {},
|
||||
statusBarHeight: 0,
|
||||
navBarHeight: 0,
|
||||
windowHeight: 0,
|
||||
safeAreaBottom: 0
|
||||
},
|
||||
|
||||
onLoad: function(options) {
|
||||
|
||||
if (!options.customId) {
|
||||
wx.showToast({
|
||||
title: '用户信息错误',
|
||||
icon: 'none'
|
||||
});
|
||||
wx.navigateBack();
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存用户ID
|
||||
this.setData({
|
||||
customId: options.customId
|
||||
});
|
||||
|
||||
// 初始化系统信息
|
||||
this.initSystemInfo();
|
||||
|
||||
// 加载用户资料
|
||||
this.loadUserProfile();
|
||||
},
|
||||
|
||||
// 初始化系统信息
|
||||
initSystemInfo: function() {
|
||||
try {
|
||||
const systemInfo = wx.getSystemInfoSync();
|
||||
const menuButtonInfo = wx.getMenuButtonBoundingClientRect();
|
||||
|
||||
this.setData({
|
||||
systemInfo: systemInfo,
|
||||
statusBarHeight: systemInfo.statusBarHeight,
|
||||
navBarHeight: menuButtonInfo.top + menuButtonInfo.height,
|
||||
windowHeight: systemInfo.windowHeight,
|
||||
safeAreaBottom: systemInfo.safeArea?.bottom || systemInfo.windowHeight
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取系统信息失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 加载用户资料
|
||||
loadUserProfile: function() {
|
||||
const { customId } = this.data;
|
||||
this.setData({ loading: true, error: '' });
|
||||
|
||||
wx.request({
|
||||
url: `${config.api.baseUrl}/api/v1/users/${customId}`,
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
|
||||
// 🔥 统一处理401 - 使用apiClient的统一处理
|
||||
const apiClient = require('../../utils/api-client.js');
|
||||
if (apiClient.is401Error && apiClient.is401Error(res)) {
|
||||
const app = getApp();
|
||||
const isLoggedIn = app?.globalData?.isLoggedIn || false;
|
||||
apiClient.handle401Error(isLoggedIn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.data.code !== 200) {
|
||||
this.setData({
|
||||
loading: false,
|
||||
error: res.data.message || '加载用户资料失败'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const userData = res.data.data.user || {};
|
||||
|
||||
// 处理头像URL
|
||||
let avatar = userData.avatar || '/images/findme-logo.png';
|
||||
if (avatar && !avatar.startsWith('http')) {
|
||||
try {
|
||||
const baseDomain = config.api.baseUrl.replace(/\/api\/v1$/, '');
|
||||
avatar = `${baseDomain}${avatar}`;
|
||||
} catch (e) {
|
||||
console.error('头像路径处理失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存头像
|
||||
imageCacheManager.cacheImage(avatar, 'user_avatar')
|
||||
.then(cachedAvatar => {
|
||||
|
||||
// 更新用户数据
|
||||
const processedUserInfo = {
|
||||
...userData,
|
||||
avatar: cachedAvatar,
|
||||
nickname: userData.nickname || userData.customId || '未知用户',
|
||||
// 格式化用户信息
|
||||
formattedGender: userData.gender === 1 ? '男' : (userData.gender === 2 ? '女' : '未设置'),
|
||||
formattedLocation: userData.location || '未设置'
|
||||
};
|
||||
|
||||
// 检查是否是当前用户
|
||||
const currentUser = app.globalData.userInfo || {};
|
||||
const isCurrentUser = currentUser.user && currentUser.user.customId === customId;
|
||||
|
||||
this.setData({
|
||||
userInfo: processedUserInfo,
|
||||
stats: res.data.data.stats || this.data.stats,
|
||||
isCurrentUser: isCurrentUser,
|
||||
isFollowing: res.data.data.isFollowing || false,
|
||||
isFriend: res.data.data.isFriend || false,
|
||||
loading: false
|
||||
});
|
||||
|
||||
// 加载用户动态
|
||||
this.loadUserFeeds();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('头像缓存失败:', error);
|
||||
// 继续处理,使用原始头像URL
|
||||
this.setData({
|
||||
userInfo: userData,
|
||||
loading: false
|
||||
});
|
||||
this.loadUserFeeds();
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('加载用户资料失败:', err);
|
||||
this.setData({
|
||||
loading: false,
|
||||
error: '网络请求失败,请检查网络连接'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 加载用户动态
|
||||
loadUserFeeds: function() {
|
||||
const { customId, currentPage, pageSize, feedList } = this.data;
|
||||
|
||||
// 防止重复加载
|
||||
if (this.data.loadingMore || !this.data.hasMoreData) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setData({ loadingMore: true });
|
||||
|
||||
wx.request({
|
||||
url: `${config.api.baseUrl}/api/v1/feeds/user/${customId}`,
|
||||
method: 'GET',
|
||||
data: {
|
||||
page: currentPage,
|
||||
pageSize: pageSize
|
||||
},
|
||||
header: {
|
||||
'Authorization': `Bearer ${apiClient.getToken() || ''}`
|
||||
},
|
||||
success: (res) => {
|
||||
|
||||
if (res.data.code === 200) {
|
||||
const newFeeds = res.data.data.feeds || [];
|
||||
|
||||
// 格式化动态数据
|
||||
const processedFeeds = newFeeds.map(feed => {
|
||||
// 格式化时间
|
||||
let formattedTime = '未知时间';
|
||||
try {
|
||||
formattedTime = this.formatTime(feed.createdAt || '');
|
||||
} catch (error) {
|
||||
console.error('时间格式化失败:', error);
|
||||
}
|
||||
|
||||
// 处理媒体URL
|
||||
let processedMedia = [];
|
||||
if (feed.media && Array.isArray(feed.media)) {
|
||||
processedMedia = feed.media.map(mediaItem => {
|
||||
let url = mediaItem.url || '';
|
||||
if (url && !url.startsWith('http') && url.startsWith('/')) {
|
||||
try {
|
||||
const baseDomain = config.api.baseUrl.replace(/\/api\/v1$/, '');
|
||||
url = `${baseDomain}${url}`;
|
||||
} catch (e) {}
|
||||
}
|
||||
return {
|
||||
...mediaItem,
|
||||
url: url
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const imageMedia = processedMedia.filter(item => item.type === 'image');
|
||||
const imageRowCount = imageMedia.length > 0 ? Math.ceil(imageMedia.length / 3) : 0;
|
||||
|
||||
return {
|
||||
...feed,
|
||||
formattedTime: formattedTime,
|
||||
media: processedMedia,
|
||||
imageMedia,
|
||||
imageRowCount
|
||||
};
|
||||
});
|
||||
|
||||
// 合并动态列表
|
||||
const updatedFeeds = currentPage === 1 ? processedFeeds : [...feedList, ...processedFeeds];
|
||||
|
||||
this.setData({
|
||||
feedList: updatedFeeds,
|
||||
currentPage: currentPage + 1,
|
||||
hasMoreData: res.data.data.hasMore || false,
|
||||
loadingMore: false
|
||||
});
|
||||
} else {
|
||||
console.error('加载用户动态失败:', res.data.message);
|
||||
this.setData({ loadingMore: false });
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('加载用户动态网络失败:', err);
|
||||
this.setData({ loadingMore: false });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime: function(timeStr) {
|
||||
if (!timeStr) return '未知时间';
|
||||
|
||||
const createTime = new Date(timeStr);
|
||||
if (isNaN(createTime.getTime())) return '未知时间';
|
||||
|
||||
const now = new Date();
|
||||
const diffMinutes = Math.floor((now - createTime) / (1000 * 60));
|
||||
|
||||
// 5分钟内:刚刚
|
||||
if (diffMinutes < 5) return '刚刚';
|
||||
|
||||
// 1小时内:xx分钟前
|
||||
if (diffMinutes < 60) return `${diffMinutes}分钟前`;
|
||||
|
||||
// 24小时内:xx小时前
|
||||
const diffHours = Math.floor(diffMinutes / 60);
|
||||
if (diffHours < 24) return `${diffHours}小时前`;
|
||||
|
||||
// 7天内:xx天前
|
||||
const diffDays = Math.floor(diffHours / 24);
|
||||
if (diffDays < 7) return `${diffDays}天前`;
|
||||
|
||||
// 格式化日期时间
|
||||
const year = createTime.getFullYear();
|
||||
const month = String(createTime.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(createTime.getDate()).padStart(2, '0');
|
||||
|
||||
// 跨年份显示完整日期,同年份省略年份
|
||||
return year === this.data.currentYear
|
||||
? `${month}月${day}日`
|
||||
: `${year}年${month}月${day}日`;
|
||||
},
|
||||
|
||||
// 点击返回按钮
|
||||
onBack: function() {
|
||||
wx.navigateBack();
|
||||
},
|
||||
|
||||
// 点击关注/取消关注
|
||||
onFollowToggle: function() {
|
||||
const { customId, isFollowing } = this.data;
|
||||
|
||||
wx.request({
|
||||
url: `${config.api.baseUrl}/api/v1/users/${customId}/follow`,
|
||||
method: isFollowing ? 'DELETE' : 'POST',
|
||||
header: {
|
||||
'Authorization': `Bearer ${apiClient.getToken() || ''}`
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.data.code === 200) {
|
||||
this.setData({
|
||||
isFollowing: !isFollowing
|
||||
});
|
||||
wx.showToast({
|
||||
title: isFollowing ? '已取消关注' : '关注成功',
|
||||
icon: 'success'
|
||||
});
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: res.data.message || '操作失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('关注操作失败:', err);
|
||||
wx.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 点击发送消息
|
||||
onSendMessage: function() {
|
||||
const { customId, userInfo } = this.data;
|
||||
wx.navigateTo({
|
||||
url: `/pages/message/chat/chat?userId=${customId}&userName=${userInfo.nickname || '未知用户'}`
|
||||
});
|
||||
},
|
||||
|
||||
// 点击添加好友
|
||||
onAddFriend: function() {
|
||||
const { customId, userInfo } = this.data;
|
||||
|
||||
wx.request({
|
||||
url: `${config.api.baseUrl}/api/v1/friends/request`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
targetUserId: customId,
|
||||
message: '我想加你为好友'
|
||||
},
|
||||
header: {
|
||||
'Authorization': `Bearer ${apiClient.getToken() || ''}`
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.data.code === 200) {
|
||||
wx.showToast({
|
||||
title: '好友请求已发送',
|
||||
icon: 'success'
|
||||
});
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: res.data.message || '发送请求失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('发送好友请求失败:', err);
|
||||
wx.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 点击动态图片
|
||||
onImageTap: function(e) {
|
||||
const { index, feedIndex } = e.currentTarget.dataset;
|
||||
const feed = this.data.feedList[feedIndex];
|
||||
if (!feed || !feed.imageMedia) return;
|
||||
|
||||
const urls = feed.imageMedia.map(item => item.url);
|
||||
if (urls.length === 0) return;
|
||||
|
||||
wx.previewImage({
|
||||
current: urls[index],
|
||||
urls: urls
|
||||
});
|
||||
},
|
||||
|
||||
// 点击动态项
|
||||
onFeedTap: function(e) {
|
||||
const { feedIndex } = e.currentTarget.dataset;
|
||||
const feed = this.data.feedList[feedIndex];
|
||||
if (!feed) return;
|
||||
|
||||
// 可以跳转到动态详情页,如果有的话
|
||||
wx.showToast({
|
||||
title: '查看动态详情',
|
||||
icon: 'none'
|
||||
});
|
||||
},
|
||||
|
||||
// 下拉刷新
|
||||
onPullDownRefresh: function() {
|
||||
this.setData({
|
||||
currentPage: 1,
|
||||
hasMoreData: true,
|
||||
feedList: []
|
||||
}, () => {
|
||||
this.loadUserProfile();
|
||||
wx.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom: function() {
|
||||
this.loadUserFeeds();
|
||||
},
|
||||
|
||||
// 分享功能
|
||||
onShareAppMessage: function() {
|
||||
const { userInfo } = this.data;
|
||||
return {
|
||||
title: `${userInfo.nickname}的个人主页`,
|
||||
path: `/pages/user-profile/user-profile?customId=${this.data.customId}`,
|
||||
imageUrl: userInfo.avatar || '/images/findme-logo.png'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
10
pages/user-profile/user-profile.json
Normal file
10
pages/user-profile/user-profile.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"navigationBarTitleText": "用户资料",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#f5f5f5",
|
||||
"enablePullDownRefresh": true,
|
||||
"usingComponents": {
|
||||
},
|
||||
"onReachBottomDistance": 50
|
||||
}
|
||||
193
pages/user-profile/user-profile.wxml
Normal file
193
pages/user-profile/user-profile.wxml
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
<view class="user-profile-page">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar" style="height: {{navBarHeight}}px; padding-top: {{statusBarHeight}}px;">
|
||||
<view class="nav-left" bindtap="onBack">
|
||||
<image class="back-icon" src="/images/back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="nav-title">个人资料</view>
|
||||
<view class="nav-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-container" wx:if="{{loading}}">
|
||||
<image class="loading-img" src="/images/loading.png" mode="aspectFit"></image>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<view class="error-container" wx:elif="{{error}}">
|
||||
<image class="error-img" src="/images/error.png" mode="aspectFit"></image>
|
||||
<text class="error-text">{{error}}</text>
|
||||
<button class="retry-btn" bindtap="loadUserProfile">重试</button>
|
||||
</view>
|
||||
|
||||
<!-- 用户资料内容 -->
|
||||
<view class="profile-content" wx:else>
|
||||
<!-- 封面区域 -->
|
||||
<view class="cover-section">
|
||||
<image class="cover-image" src="{{userInfo.coverImage || '/images/default-cover.png'}}" mode="aspectFill"></image>
|
||||
</view>
|
||||
|
||||
<!-- 用户基本信息区域 -->
|
||||
<view class="user-info-section">
|
||||
<!-- 头像和昵称信息 -->
|
||||
<view class="avatar-info">
|
||||
<image class="user-avatar" src="{{userInfo.avatar}}" mode="aspectFill"></image>
|
||||
<view class="user-basic-info">
|
||||
<text class="user-nickname">{{userInfo.nickname}}</text>
|
||||
<text class="user-id">ID: {{userInfo.customId}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<view class="action-buttons">
|
||||
<!-- 非当前用户显示的操作按钮 -->
|
||||
<block wx:if="{{!isCurrentUser}}">
|
||||
<!-- 好友状态下显示消息按钮 -->
|
||||
<block wx:if="{{isFriend}}">
|
||||
<button class="message-btn" type="primary" size="mini" bindtap="onSendMessage">
|
||||
<image class="btn-icon" src="/images/message.png" mode="aspectFit"></image>
|
||||
<text class="btn-text">发消息</text>
|
||||
</button>
|
||||
</block>
|
||||
<!-- 非好友状态下显示添加好友按钮 -->
|
||||
<block wx:elif="{{!isFriend}}">
|
||||
<button class="add-friend-btn" type="primary" size="mini" bindtap="onAddFriend">
|
||||
<image class="btn-icon" src="/images/add-friend.png" mode="aspectFit"></image>
|
||||
<text class="btn-text">加好友</text>
|
||||
</button>
|
||||
</block>
|
||||
<!-- 关注/取消关注按钮 -->
|
||||
<button class="follow-btn" type="default" size="mini" bindtap="onFollowToggle">
|
||||
<text class="btn-text">{{isFollowing ? '已关注' : '关注'}}</text>
|
||||
</button>
|
||||
</block>
|
||||
<!-- 当前用户显示编辑资料按钮 -->
|
||||
<block wx:elif="{{isCurrentUser}}">
|
||||
<button class="edit-btn" type="primary" size="mini">
|
||||
<image class="btn-icon" src="/images/edit.png" mode="aspectFit"></image>
|
||||
<text class="btn-text">编辑资料</text>
|
||||
</button>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 用户个人简介 -->
|
||||
<view class="user-intro" wx:if="{{userInfo.bio}}">
|
||||
<text class="intro-text">{{userInfo.bio}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 用户基本信息详情 -->
|
||||
<view class="user-details">
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">性别</text>
|
||||
<text class="detail-value">{{userInfo.formattedGender}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">地区</text>
|
||||
<text class="detail-value">{{userInfo.formattedLocation}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">生日</text>
|
||||
<text class="detail-value">{{userInfo.birthday || '未设置'}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">星座</text>
|
||||
<text class="detail-value">{{userInfo.constellation || '未设置'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 统计数据区域 -->
|
||||
<view class="stats-section">
|
||||
<view class="stat-item">
|
||||
<text class="stat-number">{{stats.friendsCount}}</text>
|
||||
<text class="stat-label">好友</text>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-number">{{stats.postsCount}}</text>
|
||||
<text class="stat-label">动态</text>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-number">{{stats.likesCount}}</text>
|
||||
<text class="stat-label">获赞</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 动态列表标题 -->
|
||||
<view class="feed-section-header">
|
||||
<text class="section-title">TA的动态</text>
|
||||
</view>
|
||||
|
||||
<!-- 动态列表区域 -->
|
||||
<view class="feed-section">
|
||||
<!-- 动态项 -->
|
||||
<view class="feed-item" wx:for="{{feedList}}" wx:key="id" data-feed-index="{{index}}" bindtap="onFeedTap">
|
||||
<!-- 动态内容 -->
|
||||
<view class="feed-content" wx:if="{{item.content}}">
|
||||
<text class="content-text">{{item.content}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 动态媒体(图片) -->
|
||||
<view class="feed-media" wx:if="{{item.imageMedia && item.imageMedia.length > 0}}">
|
||||
<!-- 最多3张图的网格 -->
|
||||
<view class="media-grid" wx:if="{{item.imageMedia.length <= 3}}">
|
||||
<image
|
||||
wx:for="{{item.imageMedia}}"
|
||||
wx:for-item="mediaItem"
|
||||
wx:for-index="imgIndex"
|
||||
wx:key="imgIndex"
|
||||
class="media-image small"
|
||||
src="{{mediaItem.url}}"
|
||||
mode="aspectFill"
|
||||
data-index="{{imgIndex}}"
|
||||
data-feed-index="{{index}}"
|
||||
bindtap="onImageTap"
|
||||
></image>
|
||||
</view>
|
||||
|
||||
<!-- 超过3张图的网格 -->
|
||||
<view class="media-grid" wx:else>
|
||||
<view class="media-row" wx:for="{{item.imageRowCount}}" wx:for-item="row" wx:for-index="rowIndex" wx:key="rowIndex">
|
||||
<image
|
||||
wx:for="{{item.imageMedia}}"
|
||||
wx:for-item="mediaItem"
|
||||
wx:for-index="mediaIndex"
|
||||
wx:key="mediaIndex"
|
||||
wx:if="{{mediaIndex >= rowIndex * 3 && mediaIndex < (rowIndex + 1) * 3}}"
|
||||
class="media-image grid"
|
||||
src="{{mediaItem.url}}"
|
||||
mode="aspectFill"
|
||||
data-index="{{mediaIndex}}"
|
||||
data-feed-index="{{index}}"
|
||||
bindtap="onImageTap"
|
||||
></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 动态时间 -->
|
||||
<view class="feed-footer">
|
||||
<text class="feed-time">{{item.formattedTime}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 无动态提示 -->
|
||||
<view class="no-feeds" wx:if="{{feedList.length === 0}}">
|
||||
<image class="no-feeds-img" src="/images/no-content.png" mode="aspectFit"></image>
|
||||
<text class="no-feeds-text">暂无动态</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多提示 -->
|
||||
<view class="loading-more" wx:if="{{loadingMore}}">
|
||||
<text class="loading-more-text">加载更多...</text>
|
||||
</view>
|
||||
|
||||
<!-- 没有更多数据提示 -->
|
||||
<view class="no-more" wx:if="{{!hasMoreData && feedList.length > 0}}">
|
||||
<text class="no-more-text">没有更多了</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
464
pages/user-profile/user-profile.wxss
Normal file
464
pages/user-profile/user-profile.wxss
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
/* 用户资料页面样式 */
|
||||
|
||||
/* 基础样式重置 */
|
||||
page {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 顶部导航栏 */
|
||||
.nav-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
z-index: 100;
|
||||
box-shadow: 0 1px 10px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.nav-left, .nav-right {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 0;
|
||||
}
|
||||
|
||||
.loading-img {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
animation: rotate 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-top: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 错误状态 */
|
||||
.error-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 0;
|
||||
}
|
||||
|
||||
.error-img {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
margin-top: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
margin-top: 40rpx;
|
||||
background-color: #1aad19;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
padding: 0 60rpx;
|
||||
line-height: 70rpx;
|
||||
border-radius: 35rpx;
|
||||
}
|
||||
|
||||
.retry-btn:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 封面区域 */
|
||||
.cover-section {
|
||||
width: 100%;
|
||||
height: 400rpx;
|
||||
background-color: #f0f0f0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cover-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* 用户基本信息区域 */
|
||||
.user-info-section {
|
||||
background-color: #fff;
|
||||
padding: 20rpx 30rpx 40rpx;
|
||||
margin-top: -80rpx;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 头像和昵称信息 */
|
||||
.avatar-info {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid #fff;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.user-basic-info {
|
||||
margin-left: 30rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-nickname {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
display: block;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.user-id {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 操作按钮区域 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20rpx;
|
||||
gap: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.message-btn, .add-friend-btn, .follow-btn, .edit-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 180rpx;
|
||||
height: 70rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 70rpx;
|
||||
padding: 0 30rpx;
|
||||
border-radius: 35rpx;
|
||||
}
|
||||
|
||||
.message-btn {
|
||||
background-color: #1aad19;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.add-friend-btn {
|
||||
background-color: #1aad19;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.follow-btn {
|
||||
background-color: #f0f0f0;
|
||||
color: #666;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
background-color: #1aad19;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 用户个人简介 */
|
||||
.user-intro {
|
||||
margin-top: 30rpx;
|
||||
padding: 20rpx;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.intro-text {
|
||||
font-size: 32rpx;
|
||||
line-height: 48rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 用户基本信息详情 */
|
||||
.user-details {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.detail-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: 32rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 统计数据区域 */
|
||||
.stats-section {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
padding: 30rpx 0;
|
||||
margin-top: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.stat-divider {
|
||||
width: 1rpx;
|
||||
height: 60rpx;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
/* 动态列表标题 */
|
||||
.feed-section-header {
|
||||
background-color: #fff;
|
||||
margin-top: 20rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
border-radius: 10rpx 10rpx 0 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 动态列表区域 */
|
||||
.feed-section {
|
||||
background-color: #fff;
|
||||
padding: 0 30rpx 40rpx;
|
||||
border-radius: 0 0 10rpx 10rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
/* 动态项 */
|
||||
.feed-item {
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.feed-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* 动态内容 */
|
||||
.feed-content {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
/* 为内部text标签添加换行样式 */
|
||||
.feed-content text,
|
||||
.feed-content .content-text {
|
||||
word-break: break-all !important; /* 允许在任意字符处断行 */
|
||||
word-wrap: break-word !important; /* 允许长单词换行到下一行 */
|
||||
overflow-wrap: break-word !important; /* 更现代的换行属性,支持更多浏览器 */
|
||||
white-space: pre-wrap !important; /* 保留空白并自动换行 */
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: hidden; /* 防止内容溢出容器 */
|
||||
/* 针对连续英文和数字的特殊处理 */
|
||||
-webkit-hyphens: auto; /* WebKit浏览器支持 */
|
||||
hyphens: auto; /* 允许自动断词并添加连字符 */
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-size: 32rpx;
|
||||
line-height: 48rpx;
|
||||
color: #333;
|
||||
white-space: pre-wrap !important;
|
||||
word-break: break-all !important;
|
||||
word-wrap: break-word !important;
|
||||
overflow-wrap: break-word !important;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
-webkit-hyphens: auto;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
/* 动态媒体 */
|
||||
.feed-media {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.media-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.media-row {
|
||||
display: flex;
|
||||
gap: 10rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.media-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.media-image {
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.media-image.small {
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
}
|
||||
|
||||
.media-image.grid {
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
}
|
||||
|
||||
/* 动态时间 */
|
||||
.feed-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.feed-time {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 无动态提示 */
|
||||
.no-feeds {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 0;
|
||||
}
|
||||
|
||||
.no-feeds-img {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.no-feeds-text {
|
||||
margin-top: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 加载更多提示 */
|
||||
.loading-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 30rpx 0;
|
||||
}
|
||||
|
||||
.loading-more-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 没有更多数据提示 */
|
||||
.no-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 30rpx 0;
|
||||
}
|
||||
|
||||
.no-more-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue