Эх сурвалжийг харах

feat(article-center): add my-posts page with status badges

Sisyphus 2 сар өмнө
parent
commit
b78be5880c

+ 156 - 0
cfc-frontend/pages/article-center/my-posts.vue

@@ -0,0 +1,156 @@
+<template>
+  <view class="posts-container">
+    <scroll-view scroll-y class="posts-list" @scrolltolower="onLoadMore">
+      <view v-if="loading && posts.length === 0" class="loading-wrap">
+        <text class="loading-text">加载中...</text>
+      </view>
+
+      <view v-else-if="posts.length === 0" class="empty-wrap">
+        <text class="empty-icon">📝</text>
+        <text class="empty-text">还没有发布过记录</text>
+        <button class="write-btn" @click="goEdit">写一篇</button>
+      </view>
+
+      <view v-else class="post-list">
+        <view v-for="item in posts" :key="item.id" class="post-card">
+          <image
+            v-if="item.coverImage"
+            class="post-cover"
+            :src="item.coverImage"
+            mode="aspectFill"
+          />
+          <view class="post-body">
+            <text class="post-title">{{ item.title }}</text>
+            <view class="post-meta">
+              <text class="post-date">{{ formatDate(item.publishedAt || item.createdAt) }}</text>
+              <text
+                :class="['post-status', getStatusClass(item)]"
+              >
+                {{ getStatusText(item) }}
+              </text>
+            </view>
+            <text v-if="item.auditStatus === 'rejected' && item.auditReason" class="post-reason">
+              驳回原因:{{ item.auditReason }}
+            </text>
+          </view>
+        </view>
+      </view>
+
+      <view v-if="loadingMore" class="loading-more">
+        <text class="loading-text">加载中...</text>
+      </view>
+      <view v-if="noMore && posts.length > 0" class="no-more">
+        <text class="no-more-text">— 没有更多了 —</text>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import { getMyPosts } from '@/utils/api.js'
+
+export default {
+  data() {
+    return {
+      posts: [],
+      page: 1,
+      size: 20,
+      total: 0,
+      loading: false,
+      loadingMore: false,
+      noMore: false
+    }
+  },
+  onLoad() {
+    this.loadPosts()
+  },
+  methods: {
+    async loadPosts(isLoadMore) {
+      if (!isLoadMore) {
+        if (this.loading) return
+        this.loading = true
+      } else {
+        if (this.loadingMore || this.noMore) return
+        this.loadingMore = true
+      }
+      try {
+        var res = await getMyPosts({ page: this.page, size: this.size })
+        if (res.code === 200 && res.data) {
+          var list = res.data.records || []
+          if (this.page === 1) {
+            this.posts = list
+          } else {
+            this.posts = this.posts.concat(list)
+          }
+          this.total = res.data.total || 0
+          this.noMore = this.posts.length >= this.total
+        }
+      } catch (e) {
+        uni.showToast({ title: '加载失败', icon: 'none' })
+      } finally {
+        this.loading = false
+        this.loadingMore = false
+      }
+    },
+    onLoadMore() {
+      if (this.noMore || this.loading || this.loadingMore) return
+      this.page++
+      this.loadPosts(true)
+    },
+    goEdit() {
+      uni.navigateTo({ url: '/pages/article-center/article-edit' })
+    },
+    formatDate(dateStr) {
+      if (!dateStr) return ''
+      return dateStr.slice(0, 10)
+    },
+    getStatusText(item) {
+      if (item.status === 'draft' && (!item.auditStatus || item.auditStatus === 'pending')) {
+        return '审核中'
+      }
+      if (item.auditStatus === 'rejected') return '已驳回'
+      if (item.status === 'published') return '已发布'
+      return '草稿'
+    },
+    getStatusClass(item) {
+      if (item.auditStatus === 'approved' || item.status === 'published') return 'status-approved'
+      if (item.auditStatus === 'pending') return 'status-pending'
+      if (item.auditStatus === 'rejected') return 'status-rejected'
+      return 'status-draft'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.posts-container { min-height: 100vh; background: #f5f7fa; }
+.posts-list { height: 100vh; }
+.loading-wrap, .empty-wrap { display: flex; flex-direction: column; align-items: center; padding-top: 200rpx; }
+.loading-text { font-size: 26rpx; color: #999; }
+.empty-icon { font-size: 100rpx; margin-bottom: 24rpx; }
+.empty-text { font-size: 28rpx; color: #999; margin-bottom: 30rpx; }
+.write-btn { width: 240rpx; height: 72rpx; line-height: 72rpx; background: #5B9BD5; color: #fff; font-size: 28rpx; border-radius: 36rpx; border: none; }
+.write-btn::after { border: none; }
+.post-list { padding: 20rpx 24rpx; }
+.post-card {
+  display: flex;
+  background: #fff;
+  border-radius: 16rpx;
+  overflow: hidden;
+  margin-bottom: 20rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
+}
+.post-cover { width: 200rpx; height: 160rpx; flex-shrink: 0; }
+.post-body { flex: 1; padding: 20rpx; display: flex; flex-direction: column; justify-content: center; }
+.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; }
+.post-meta { display: flex; align-items: center; font-size: 22rpx; }
+.post-date { color: #999; margin-right: 16rpx; }
+.post-status { padding: 2rpx 12rpx; border-radius: 8rpx; }
+.status-approved { color: #22c55e; background: rgba(34,197,94,0.1); }
+.status-pending { color: #f59e0b; background: rgba(245,158,11,0.1); }
+.status-rejected { color: #ef4444; background: rgba(239,68,68,0.1); }
+.status-draft { color: #999; background: rgba(153,153,153,0.1); }
+.post-reason { font-size: 20rpx; color: #ef4444; margin-top: 6rpx; display: block; }
+.loading-more, .no-more { text-align: center; padding: 30rpx; }
+.no-more-text { font-size: 24rpx; color: #ccc; }
+</style>