瑶都万能墙
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

475 lines
10 KiB

2 weeks ago
  1. <template>
  2. <view class="fans-list-page">
  3. <navbar title="粉丝列表" leftClick @leftClick="$utils.navigateBack" />
  4. <!-- 标签页切换 -->
  5. <view class="tabs-container">
  6. <uv-tabs
  7. :list="tabsList"
  8. :current="currentTab"
  9. :activeStyle="{color: '#333', fontWeight: 600}"
  10. lineColor="#5baaff"
  11. lineHeight="6rpx"
  12. lineWidth="40rpx"
  13. keyName="name"
  14. @click="onTabClick"
  15. />
  16. </view>
  17. <!-- 用户列表 -->
  18. <view class="user-list">
  19. <view
  20. class="user-item"
  21. v-for="(user, index) in userList"
  22. :key="index"
  23. @click="goToUserProfile(user)"
  24. >
  25. <view class="user-avatar">
  26. <image :src="user.headImage" mode="aspectFill"></image>
  27. </view>
  28. <view class="user-info">
  29. <view class="user-name">{{ user.nickName }}</view>
  30. <view class="user-desc">
  31. <view class="user-tags">
  32. <text class="tag" v-if="user.sex">{{ user.sex }}</text>
  33. <text class="tag" v-if="user.address">{{ user.address }}</text>
  34. <text class="auth-tag" v-if="user.idCardOpen">{{ getAuthText(user.idCardOpen) }}</text>
  35. </view>
  36. </view>
  37. </view>
  38. <view class="user-action" v-if="!isCurrentUserPage">
  39. <button
  40. class="follow-btn"
  41. :class="{followed: user.isFollowed}"
  42. @click.stop="toggleFollow(user, index)"
  43. >
  44. {{ user.isFollowed ? '已关注' : '关注' }}
  45. </button>
  46. </view>
  47. </view>
  48. </view>
  49. <!-- 空状态 -->
  50. <view v-if="!loading && userList.length === 0" class="empty-state">
  51. <uv-empty
  52. :text="emptyText"
  53. icon="account"
  54. iconSize="120"
  55. ></uv-empty>
  56. </view>
  57. <!-- 加载状态 -->
  58. <view v-if="loading" class="loading-state">
  59. <uv-loading-icon size="40"></uv-loading-icon>
  60. <text>加载中...</text>
  61. </view>
  62. </view>
  63. </template>
  64. <script>
  65. import { mapState } from 'vuex'
  66. export default {
  67. data() {
  68. return {
  69. userId: '', // 用户ID
  70. type: 'fans', // fans: 粉丝, following: 关注
  71. currentTab: 0,
  72. tabsList: [
  73. { name: '粉丝', type: 'fans' },
  74. { name: '关注', type: 'following' }
  75. ],
  76. userList: [], // 用户列表
  77. loading: false,
  78. // 模拟数据
  79. mockFansData: [
  80. {
  81. id: '1',
  82. nickName: '小美同学',
  83. headImage: 'https://picsum.photos/100/100?random=1',
  84. sex: '女',
  85. address: '北京',
  86. idCardOpen: 1,
  87. isFollowed: false
  88. },
  89. {
  90. id: '2',
  91. nickName: '程序员小王',
  92. headImage: 'https://picsum.photos/100/100?random=2',
  93. sex: '男',
  94. address: '上海',
  95. idCardOpen: 2,
  96. isFollowed: true
  97. },
  98. {
  99. id: '3',
  100. nickName: '设计师小李',
  101. headImage: 'https://picsum.photos/100/100?random=3',
  102. sex: '女',
  103. address: '深圳',
  104. idCardOpen: 0,
  105. isFollowed: false
  106. },
  107. {
  108. id: '4',
  109. nickName: '产品经理老张',
  110. headImage: 'https://picsum.photos/100/100?random=4',
  111. sex: '男',
  112. address: '广州',
  113. idCardOpen: 1,
  114. isFollowed: true
  115. },
  116. {
  117. id: '5',
  118. nickName: '运营小姐姐',
  119. headImage: 'https://picsum.photos/100/100?random=5',
  120. sex: '女',
  121. address: '杭州',
  122. idCardOpen: 2,
  123. isFollowed: false
  124. },
  125. {
  126. id: '6',
  127. nickName: '前端工程师',
  128. headImage: 'https://picsum.photos/100/100?random=6',
  129. sex: '男',
  130. address: '成都',
  131. idCardOpen: 1,
  132. isFollowed: true
  133. },
  134. {
  135. id: '7',
  136. nickName: 'UI设计师',
  137. headImage: 'https://picsum.photos/100/100?random=7',
  138. sex: '女',
  139. address: '武汉',
  140. idCardOpen: 0,
  141. isFollowed: false
  142. },
  143. {
  144. id: '8',
  145. nickName: '后端大神',
  146. headImage: 'https://picsum.photos/100/100?random=8',
  147. sex: '男',
  148. address: '西安',
  149. idCardOpen: 2,
  150. isFollowed: true
  151. }
  152. ],
  153. mockFollowingData: [
  154. {
  155. id: '9',
  156. nickName: '技术大牛',
  157. headImage: 'https://picsum.photos/100/100?random=9',
  158. sex: '男',
  159. address: '北京',
  160. idCardOpen: 2,
  161. isFollowed: true
  162. },
  163. {
  164. id: '10',
  165. nickName: '设计总监',
  166. headImage: 'https://picsum.photos/100/100?random=10',
  167. sex: '女',
  168. address: '上海',
  169. idCardOpen: 2,
  170. isFollowed: true
  171. },
  172. {
  173. id: '11',
  174. nickName: '产品总监',
  175. headImage: 'https://picsum.photos/100/100?random=11',
  176. sex: '男',
  177. address: '深圳',
  178. idCardOpen: 1,
  179. isFollowed: true
  180. },
  181. {
  182. id: '12',
  183. nickName: '运营总监',
  184. headImage: 'https://picsum.photos/100/100?random=12',
  185. sex: '女',
  186. address: '广州',
  187. idCardOpen: 2,
  188. isFollowed: true
  189. },
  190. {
  191. id: '13',
  192. nickName: '架构师',
  193. headImage: 'https://picsum.photos/100/100?random=13',
  194. sex: '男',
  195. address: '杭州',
  196. idCardOpen: 1,
  197. isFollowed: true
  198. },
  199. {
  200. id: '14',
  201. nickName: '资深设计师',
  202. headImage: 'https://picsum.photos/100/100?random=14',
  203. sex: '女',
  204. address: '成都',
  205. idCardOpen: 2,
  206. isFollowed: true
  207. }
  208. ]
  209. }
  210. },
  211. computed: {
  212. ...mapState(['userInfo']),
  213. // 页面标题
  214. pageTitle() {
  215. return this.type === 'fans' ? '粉丝列表' : '关注列表'
  216. },
  217. // 空状态文本
  218. emptyText() {
  219. return this.type === 'fans' ? '暂无粉丝' : '暂无关注'
  220. },
  221. // 是否是当前用户的页面
  222. isCurrentUserPage() {
  223. return this.userId === this.userInfo.id
  224. }
  225. },
  226. onLoad(options) {
  227. this.userId = options.userId || this.userInfo.id
  228. this.type = options.type || 'fans'
  229. // 设置初始tab
  230. this.currentTab = this.type === 'fans' ? 0 : 1
  231. this.loadUserList()
  232. },
  233. onPullDownRefresh() {
  234. this.loadUserList()
  235. },
  236. methods: {
  237. // 返回上一页
  238. goBack() {
  239. uni.navigateBack()
  240. },
  241. // 标签页切换
  242. onTabClick(item) {
  243. this.currentTab = item.index
  244. this.type = item.type
  245. this.loadUserList()
  246. },
  247. // 获取认证文本
  248. getAuthText(status) {
  249. const authTexts = ['审核中', '个人认证', '店铺认证']
  250. return authTexts[status] || '未认证'
  251. },
  252. // 跳转到用户主页
  253. goToUserProfile(user) {
  254. uni.navigateTo({
  255. url: `/pages_order/profile/userProfile?userId=${user.id}`
  256. })
  257. },
  258. // 切换关注状态
  259. toggleFollow(user, index) {
  260. if (!uni.getStorageSync('token')) {
  261. uni.showToast({
  262. title: '请先登录',
  263. icon: 'none'
  264. })
  265. return
  266. }
  267. const isFollowed = !user.isFollowed
  268. // 模拟API调用
  269. setTimeout(() => {
  270. // 更新本地数据
  271. this.userList[index].isFollowed = isFollowed
  272. // 同时更新模拟数据源
  273. if (this.type === 'fans') {
  274. const mockIndex = this.mockFansData.findIndex(item => item.id === user.id)
  275. if (mockIndex !== -1) {
  276. this.mockFansData[mockIndex].isFollowed = isFollowed
  277. }
  278. } else {
  279. const mockIndex = this.mockFollowingData.findIndex(item => item.id === user.id)
  280. if (mockIndex !== -1) {
  281. this.mockFollowingData[mockIndex].isFollowed = isFollowed
  282. }
  283. }
  284. uni.showToast({
  285. title: isFollowed ? '关注成功' : '取消关注',
  286. icon: 'success'
  287. })
  288. }, 500)
  289. },
  290. // 加载用户列表
  291. loadUserList() {
  292. this.loading = true
  293. // 模拟网络延迟
  294. setTimeout(() => {
  295. this.loading = false
  296. uni.stopPullDownRefresh()
  297. // 使用模拟数据
  298. if (this.type === 'fans') {
  299. this.userList = [...this.mockFansData]
  300. } else {
  301. this.userList = [...this.mockFollowingData]
  302. }
  303. // 模拟API调用成功
  304. console.log(`加载${this.type === 'fans' ? '粉丝' : '关注'}列表成功`)
  305. }, 1000)
  306. }
  307. }
  308. }
  309. </script>
  310. <style scoped lang="scss">
  311. .fans-list-page {
  312. background-color: #f5f5f5;
  313. min-height: 100vh;
  314. }
  315. .navbar {
  316. position: fixed;
  317. top: 0;
  318. left: 0;
  319. right: 0;
  320. z-index: 100;
  321. height: 88rpx;
  322. background: #fff;
  323. border-bottom: 1rpx solid #eee;
  324. display: flex;
  325. align-items: center;
  326. justify-content: space-between;
  327. padding: 0 30rpx;
  328. padding-top: var(--status-bar-height, 44rpx);
  329. .nav-title {
  330. color: #333;
  331. font-size: 32rpx;
  332. font-weight: 600;
  333. }
  334. .nav-left, .nav-right {
  335. width: 60rpx;
  336. height: 60rpx;
  337. display: flex;
  338. align-items: center;
  339. justify-content: center;
  340. }
  341. }
  342. .tabs-container {
  343. margin-top: 88rpx;
  344. padding-top: var(--status-bar-height, 44rpx);
  345. background: #fff;
  346. border-bottom: 1rpx solid #eee;
  347. padding-left: 20rpx;
  348. padding-right: 20rpx;
  349. }
  350. .user-list {
  351. padding: 20rpx;
  352. .user-item {
  353. display: flex;
  354. align-items: center;
  355. background: #fff;
  356. padding: 30rpx;
  357. margin-bottom: 20rpx;
  358. border-radius: 16rpx;
  359. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
  360. .user-avatar {
  361. width: 100rpx;
  362. height: 100rpx;
  363. border-radius: 50rpx;
  364. overflow: hidden;
  365. margin-right: 30rpx;
  366. image {
  367. width: 100%;
  368. height: 100%;
  369. }
  370. }
  371. .user-info {
  372. flex: 1;
  373. .user-name {
  374. font-size: 32rpx;
  375. font-weight: 600;
  376. color: #333;
  377. margin-bottom: 12rpx;
  378. }
  379. .user-desc {
  380. .user-tags {
  381. display: flex;
  382. flex-wrap: wrap;
  383. gap: 8rpx;
  384. .tag {
  385. background: #f0f0f0;
  386. color: #666;
  387. padding: 4rpx 12rpx;
  388. border-radius: 12rpx;
  389. font-size: 20rpx;
  390. }
  391. .auth-tag {
  392. background: #52c41a;
  393. color: #fff;
  394. padding: 4rpx 12rpx;
  395. border-radius: 12rpx;
  396. font-size: 20rpx;
  397. }
  398. }
  399. }
  400. }
  401. .user-action {
  402. .follow-btn {
  403. background: #5baaff;
  404. color: #fff;
  405. border: none;
  406. padding: 16rpx 32rpx;
  407. border-radius: 30rpx;
  408. font-size: 24rpx;
  409. &.followed {
  410. background: #f0f0f0;
  411. color: #666;
  412. }
  413. }
  414. }
  415. }
  416. }
  417. .empty-state {
  418. padding: 100rpx 0;
  419. text-align: center;
  420. }
  421. .loading-state {
  422. display: flex;
  423. flex-direction: column;
  424. align-items: center;
  425. padding: 60rpx 0;
  426. text {
  427. margin-top: 20rpx;
  428. font-size: 28rpx;
  429. color: #999;
  430. }
  431. }
  432. </style>