本次新增由后台 virtualPayAudit 开关控制的审核模式,在该模式下: 1. 隐藏底部 Tab 栏的发布按钮 2. 隐藏空状态、分类页、个人页的发布入口 3. 隐藏帖子卡片、详情页、个人发布页的评论数及评价入口 4. 发布页在审核模式下直接拦截并跳转至首页 5. 详情页的用户评价及评价弹层在审核模式下隐藏,避免 UGC 判定
365 行
9.3 KiB
Vue
365 行
9.3 KiB
Vue
<template>
|
||
<view class="mine-post fc-page">
|
||
<!-- 规范化顶部导航 -->
|
||
<fc-navbar title="我的发布" theme="gradient" :show-back="true" />
|
||
<!-- 状态筛选 + 计数(后台审核/下架后状态一目了然) -->
|
||
<scroll-view class="mine-post__tabs" scroll-x :show-scrollbar="false">
|
||
<view class="mine-post__tabs-inner">
|
||
<view v-for="t in tabs" :key="t.value" class="mine-post__tab"
|
||
:class="{ 'mine-post__tab--active': status === t.value }" @click="changeStatus(t.value)">
|
||
<text>{{ t.label }}</text>
|
||
<text class="mine-post__tab-num">{{ counts[t.value] || 0 }}</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<view v-if="loading && !list.length" class="mine-post__loading">
|
||
<view v-for="i in 3" :key="i" class="fc-skeleton mine-post__skeleton"></view>
|
||
</view>
|
||
|
||
<fc-empty v-else-if="!list.length" text="还没有发布信息" :btn-text="isAudit ? '' : '去发布'" @action="goCreate" />
|
||
|
||
<view v-else class="mine-post__list">
|
||
<view v-for="p in list" :key="p.id" class="fc-card mine-post__item" @click="goDetail(p)">
|
||
<view class="mine-post__head">
|
||
<text class="mine-post__title fc-ellipsis-2">{{ p.title }}</text>
|
||
<text class="mine-post__status" :class="statusClass(p)">{{ statusText(p) }}</text>
|
||
</view>
|
||
<view class="mine-post__meta">
|
||
<text v-if="p.isTop" class="mine-post__badge">置顶</text>
|
||
<text class="mine-post__meta-text">{{ p.categoryName || '未分类' }}</text>
|
||
<text class="mine-post__dot">·</text>
|
||
<text class="mine-post__meta-text">{{ p.area || '未选区域' }}</text>
|
||
<text class="mine-post__dot">·</text>
|
||
<text class="mine-post__meta-text">{{ timeText(p.createdAt) }}</text>
|
||
</view>
|
||
<view v-if="p.status === 0" class="mine-post__reason">该信息已被后台下架</view>
|
||
<view v-else-if="p.auditStatus === 2" class="mine-post__reason">
|
||
驳回原因:{{ p.auditReason || '未填写' }}
|
||
</view>
|
||
<view class="mine-post__foot">
|
||
<view class="mine-post__stats">
|
||
<text>{{ p.views }} 浏览</text>
|
||
<text v-if="!isAudit" class="mine-post__dot">·</text>
|
||
<text v-if="!isAudit">{{ p.commentCount || 0 }} 评价</text>
|
||
<text v-if="p.isPaid" class="mine-post__dot">·</text>
|
||
<text v-if="p.isPaid" class="mine-post__paid">已付费 ¥{{ p.amount }}</text>
|
||
</view>
|
||
<view class="mine-post__ops">
|
||
<text v-if="p.auditStatus === 2" class="mine-post__op mine-post__op--edit" @click.stop="goEdit(p)">重新编辑</text>
|
||
<text class="mine-post__op" @click.stop="goDetail(p)">查看</text>
|
||
<text class="mine-post__op mine-post__op--danger" @click.stop="remove(p)">删除</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="mine-post__more">
|
||
<text v-if="loading">加载中…</text>
|
||
<text v-else-if="!hasMore">— 没有更多了 —</text>
|
||
<text v-else @click="loadMore">加载更多</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="!isAudit" class="mine-post__fab" @click="goCreate">
|
||
<text>+ 发布</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import FcNavbar from '@/components/fc-navbar/fc-navbar.vue'
|
||
import FcEmpty from '@/components/fc-empty/fc-empty.vue'
|
||
import postApi from '@/common/api/post.js'
|
||
import { fromNow } from '@/common/utils/format.js'
|
||
import { uiState } from '@/common/store/ui.js'
|
||
|
||
export default {
|
||
components: { FcNavbar, FcEmpty },
|
||
computed: {
|
||
/**
|
||
* 是否处于提审防审模式(由后台 virtualPayAudit 开关统一控制)
|
||
* @returns {boolean} true=审核中(隐藏发布与评价)
|
||
*/
|
||
isAudit() {
|
||
return uiState.virtualPayAudit !== false
|
||
},
|
||
},
|
||
data() {
|
||
return {
|
||
tabs: [
|
||
{ label: '全部', value: 'all' },
|
||
{ label: '待审核', value: 'pending' },
|
||
{ label: '已通过', value: 'passed' },
|
||
{ label: '已驳回', value: 'rejected' },
|
||
{ label: '已下架', value: 'offline' },
|
||
],
|
||
status: 'all',
|
||
list: [],
|
||
counts: {},
|
||
page: 1,
|
||
size: 10,
|
||
hasMore: true,
|
||
loading: false,
|
||
}
|
||
},
|
||
onLoad() {
|
||
this.fetch(true)
|
||
},
|
||
onShow() {
|
||
// 发布/编辑/后台审核后回到本页,需要看到最新状态与计数
|
||
this.page = 1
|
||
this.fetch(true)
|
||
},
|
||
onReachBottom() {
|
||
this.loadMore()
|
||
},
|
||
methods: {
|
||
timeText(t) {
|
||
return fromNow(t)
|
||
},
|
||
async fetch(reset = false) {
|
||
this.loading = true
|
||
try {
|
||
const res = await postApi.mine({ status: this.status, page: this.page, size: this.size })
|
||
this.list = reset ? res.list : this.list.concat(res.list)
|
||
this.hasMore = !!res.hasMore
|
||
this.counts = res.counts || {}
|
||
} catch (e) {
|
||
uni.showToast({ title: e.message || '加载失败', icon: 'none' })
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
loadMore() {
|
||
if (!this.hasMore || this.loading) return
|
||
this.page += 1
|
||
this.fetch()
|
||
},
|
||
changeStatus(v) {
|
||
if (this.status === v) return
|
||
this.status = v
|
||
this.page = 1
|
||
this.fetch(true)
|
||
},
|
||
statusText(p) {
|
||
if (Number(p.status) === 0) return '已下架'
|
||
const m = { 0: '待审核', 1: '已通过', 2: '已驳回' }
|
||
return m[Number(p.auditStatus)] || '待审核'
|
||
},
|
||
statusClass(p) {
|
||
if (Number(p.status) === 0) return 'mine-post__status--offline'
|
||
const m = { 0: 'mine-post__status--pending', 1: 'mine-post__status--passed', 2: 'mine-post__status--rejected' }
|
||
return m[Number(p.auditStatus)] || 'mine-post__status--pending'
|
||
},
|
||
goDetail(p) {
|
||
uni.navigateTo({ url: `/pages/detail/detail?id=${p.id}` })
|
||
},
|
||
/**
|
||
* 跳转发布页(审核模式拦截)
|
||
*/
|
||
goCreate() {
|
||
if (this.isAudit) return
|
||
uni.navigateTo({ url: '/pages/post/create' })
|
||
},
|
||
goEdit(p) {
|
||
uni.navigateTo({
|
||
url: `/pages/post/create?editId=${p.id}&categoryId=${p.categoryId || ''}&title=${encodeURIComponent(p.title || '')}`,
|
||
})
|
||
},
|
||
remove(p) {
|
||
uni.showModal({
|
||
title: '删除信息',
|
||
content: `确认删除「${p.title}」?删除后不可恢复`,
|
||
success: async (r) => {
|
||
if (!r.confirm) return
|
||
try {
|
||
await postApi.remove(p.id)
|
||
uni.showToast({ title: '已删除', icon: 'none' })
|
||
this.page = 1
|
||
this.fetch(true)
|
||
} catch (e) {
|
||
uni.showToast({ title: e.message || '删除失败', icon: 'none' })
|
||
}
|
||
},
|
||
})
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.mine-post {
|
||
padding-bottom: 180rpx;
|
||
}
|
||
.mine-post__tabs {
|
||
background: $fc-bg-card;
|
||
white-space: nowrap;
|
||
padding: 16rpx 0;
|
||
border-bottom: 2rpx solid $fc-border;
|
||
}
|
||
.mine-post__tabs-inner {
|
||
display: inline-flex;
|
||
padding: 0 16rpx;
|
||
}
|
||
.mine-post__tab {
|
||
margin: 0 8rpx;
|
||
padding: 10rpx 24rpx;
|
||
border-radius: $fc-radius-pill;
|
||
background: $fc-bg;
|
||
font-size: $fc-font-sm;
|
||
color: $fc-text-regular;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
flex: 0 0 auto;
|
||
&--active {
|
||
background: $fc-gradient-primary;
|
||
color: $fc-primary-ink;
|
||
font-weight: 700;
|
||
box-shadow: 0 6rpx 16rpx rgba(4, 48, 30, 0.28);
|
||
}
|
||
}
|
||
.mine-post__tab-num {
|
||
margin-left: 8rpx;
|
||
font-size: 22rpx;
|
||
opacity: 0.75;
|
||
}
|
||
.mine-post__loading {
|
||
padding: 24rpx;
|
||
}
|
||
.mine-post__skeleton {
|
||
height: 180rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
.mine-post__list {
|
||
padding: 24rpx;
|
||
}
|
||
.mine-post__item {
|
||
margin-bottom: 20rpx;
|
||
}
|
||
.mine-post__head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
}
|
||
.mine-post__title {
|
||
flex: 1;
|
||
font-size: $fc-font-lg;
|
||
font-weight: 700;
|
||
color: $fc-text;
|
||
line-height: 1.4;
|
||
}
|
||
.mine-post__status {
|
||
margin-left: 16rpx;
|
||
flex: 0 0 auto;
|
||
padding: 4rpx 14rpx;
|
||
border-radius: $fc-radius-sm;
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
&--pending {
|
||
background: $fc-accent-lighter;
|
||
color: $fc-accent-dark;
|
||
}
|
||
&--passed {
|
||
background: $fc-primary-lighter;
|
||
color: $fc-primary-text;
|
||
}
|
||
&--rejected {
|
||
background: #fdeaea;
|
||
color: $fc-danger;
|
||
}
|
||
&--offline {
|
||
background: $fc-bg;
|
||
color: $fc-text-secondary;
|
||
}
|
||
}
|
||
.mine-post__meta {
|
||
margin-top: 14rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
font-size: $fc-font-sm;
|
||
color: $fc-text-secondary;
|
||
}
|
||
.mine-post__badge {
|
||
margin-right: 12rpx;
|
||
padding: 2rpx 12rpx;
|
||
border-radius: $fc-radius-sm;
|
||
background: $fc-gradient-primary;
|
||
color: $fc-primary-ink;
|
||
font-size: 20rpx;
|
||
font-weight: 700;
|
||
}
|
||
.mine-post__dot {
|
||
margin: 0 8rpx;
|
||
color: $fc-text-placeholder;
|
||
}
|
||
.mine-post__reason {
|
||
margin-top: 12rpx;
|
||
padding: 12rpx 16rpx;
|
||
border-radius: $fc-radius-sm;
|
||
background: #fdf4f4;
|
||
color: $fc-danger;
|
||
font-size: $fc-font-sm;
|
||
line-height: 1.5;
|
||
}
|
||
.mine-post__foot {
|
||
margin-top: 16rpx;
|
||
padding-top: 16rpx;
|
||
border-top: 2rpx solid $fc-border;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
.mine-post__stats {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 24rpx;
|
||
color: $fc-text-secondary;
|
||
}
|
||
.mine-post__paid {
|
||
color: $fc-price;
|
||
}
|
||
.mine-post__ops {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
.mine-post__op {
|
||
margin-left: 28rpx;
|
||
font-size: $fc-font-sm;
|
||
color: $fc-primary-text;
|
||
font-weight: 600;
|
||
&--danger {
|
||
color: $fc-danger;
|
||
}
|
||
}
|
||
.mine-post__more {
|
||
text-align: center;
|
||
padding: 24rpx 0 40rpx;
|
||
font-size: $fc-font-sm;
|
||
color: $fc-text-placeholder;
|
||
}
|
||
.mine-post {
|
||
min-height: 100vh;
|
||
padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
|
||
background: #f8fafc;
|
||
}
|
||
.mine-post__fab {
|
||
position: fixed;
|
||
right: 32rpx;
|
||
bottom: calc(60rpx + env(safe-area-inset-bottom));
|
||
padding: 18rpx 36rpx;
|
||
border-radius: $fc-radius-pill;
|
||
background: $fc-gradient-primary;
|
||
color: $fc-primary-ink;
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
box-shadow: 0 8rpx 24rpx rgba(0, 168, 85, 0.4);
|
||
cursor: pointer;
|
||
z-index: 50;
|
||
transition: transform 0.15s ease;
|
||
&:active {
|
||
transform: scale(0.92);
|
||
}
|
||
}
|
||
</style>
|