my-posts.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. <template>
  2. <view class="posts-container">
  3. <scroll-view scroll-y class="posts-list" @scrolltolower="onLoadMore">
  4. <view v-if="loading && posts.length === 0" class="loading-wrap">
  5. <text class="loading-text">加载�..</text>
  6. </view>
  7. <view v-else-if="posts.length === 0" class="empty-wrap">
  8. <text class="empty-icon">�</text>
  9. <text class="empty-text">还没有�布过记录</text>
  10. <button class="write-btn" @click="goEdit">写一�/button>
  11. </view>
  12. <view v-else class="post-list">
  13. <view v-for="item in posts" :key="item.id" class="post-card">
  14. <image
  15. v-if="item.coverImage"
  16. class="post-cover"
  17. :src="item.coverImage"
  18. mode="aspectFill"
  19. />
  20. <view class="post-body">
  21. <text class="post-title">{{ item.title }}</text>
  22. <view class="post-meta">
  23. <text class="post-date">{{ formatDate(item.publishedAt || item.createdAt) }}</text>
  24. <text
  25. :class="['post-status', getStatusClass(item)]"
  26. >
  27. {{ getStatusText(item) }}
  28. </text>
  29. </view>
  30. <text v-if="item.auditStatus === 'rejected' && item.auditReason" class="post-reason">
  31. 驳回原因:{{ item.auditReason }}
  32. </text>
  33. </view>
  34. </view>
  35. </view>
  36. <view v-if="loadingMore" class="loading-more">
  37. <text class="loading-text">加载�..</text>
  38. </view>
  39. <view v-if="noMore && posts.length > 0" class="no-more">
  40. <text class="no-more-text">�没有更多��/text>
  41. </view>
  42. </scroll-view>
  43. </view>
  44. </template>
  45. <script>
  46. import { getMyPosts } from '@/utils/api.js'
  47. export default {
  48. data() {
  49. return {
  50. posts: [],
  51. page: 1,
  52. size: 20,
  53. total: 0,
  54. loading: false,
  55. loadingMore: false,
  56. noMore: false
  57. }
  58. },
  59. onLoad() {
  60. this.loadPosts()
  61. },
  62. methods: {
  63. async loadPosts(isLoadMore) {
  64. if (!isLoadMore) {
  65. if (this.loading) return
  66. this.loading = true
  67. } else {
  68. if (this.loadingMore || this.noMore) return
  69. this.loadingMore = true
  70. }
  71. try {
  72. var res = await getMyPosts({ page: this.page, size: this.size })
  73. if (res.code === 200 && res.data) {
  74. var list = res.data.records || []
  75. if (this.page === 1) {
  76. this.posts = list
  77. } else {
  78. this.posts = this.posts.concat(list)
  79. }
  80. this.total = res.data.total || 0
  81. this.noMore = this.posts.length >= this.total
  82. }
  83. } catch (e) {
  84. uni.showToast({ title: '加载失败', icon: 'none' })
  85. } finally {
  86. this.loading = false
  87. this.loadingMore = false
  88. }
  89. },
  90. onLoadMore() {
  91. if (this.noMore || this.loading || this.loadingMore) return
  92. this.page++
  93. this.loadPosts(true)
  94. },
  95. goEdit() {
  96. uni.navigateTo({ url: '/pages/article-center/article-edit' })
  97. },
  98. formatDate(dateStr) {
  99. if (!dateStr) return ''
  100. return dateStr.slice(0, 10)
  101. },
  102. getStatusText(item) {
  103. if (item.status === 'draft' && (!item.auditStatus || item.auditStatus === 'pending')) {
  104. return '审核�
  105. }
  106. if (item.auditStatus === 'rejected') return '已驳�
  107. if (item.status === 'published') return '已��
  108. return '�稿'
  109. },
  110. getStatusClass(item) {
  111. if (item.auditStatus === 'approved' || item.status === 'published') return 'status-approved'
  112. if (item.auditStatus === 'pending') return 'status-pending'
  113. if (item.auditStatus === 'rejected') return 'status-rejected'
  114. return 'status-draft'
  115. }
  116. }
  117. }
  118. </script>
  119. <style scoped>
  120. .posts-container { min-height: 100vh; background: #f5f7fa; }
  121. .posts-list { height: 100vh; }
  122. .loading-wrap, .empty-wrap { display: flex; flex-direction: column; align-items: center; padding-top: 200rpx; }
  123. .loading-text { font-size: 26rpx; color: #999; }
  124. .empty-icon { font-size: 100rpx; margin-bottom: 24rpx; }
  125. .empty-text { font-size: 28rpx; color: #999; margin-bottom: 30rpx; }
  126. .write-btn { width: 240rpx; height: 72rpx; line-height: 72rpx; background: #5B9BD5; color: #fff; font-size: 28rpx; border-radius: 36rpx; border: none; }
  127. .write-btn::after { border: none; }
  128. .post-list { padding: 20rpx 24rpx; }
  129. .post-card {
  130. display: flex;
  131. background: #fff;
  132. border-radius: 16rpx;
  133. overflow: hidden;
  134. margin-bottom: 20rpx;
  135. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
  136. }
  137. .post-cover { width: 200rpx; height: 160rpx; flex-shrink: 0; }
  138. .post-body { flex: 1; padding: 20rpx; display: flex; flex-direction: column; justify-content: center; }
  139. .post-title { font-size: 28rpx; font-weight: bold; color: #333; line-height: 1.4; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-bottom: 8rpx; }
  140. .post-meta { display: flex; align-items: center; font-size: 22rpx; }
  141. .post-date { color: #999; margin-right: 16rpx; }
  142. .post-status { padding: 2rpx 12rpx; border-radius: 8rpx; }
  143. .status-approved { color: #22c55e; background: rgba(34,197,94,0.1); }
  144. .status-pending { color: #f59e0b; background: rgba(245,158,11,0.1); }
  145. .status-rejected { color: #ef4444; background: rgba(239,68,68,0.1); }
  146. .status-draft { color: #999; background: rgba(153,153,153,0.1); }
  147. .post-reason { font-size: 20rpx; color: #ef4444; margin-top: 6rpx; display: block; }
  148. .loading-more, .no-more { text-align: center; padding: 30rpx; }
  149. .no-more-text { font-size: 24rpx; color: #ccc; }
  150. </style>