文件
fenleixinxi/uniapp/pages/post/mine.vue
MiAn 14d02aedcd feat: 新增提审防审模式,隐藏发布及评论相关入口
本次新增由后台 virtualPayAudit 开关控制的审核模式,在该模式下:
1. 隐藏底部 Tab 栏的发布按钮
2. 隐藏空状态、分类页、个人页的发布入口
3. 隐藏帖子卡片、详情页、个人发布页的评论数及评价入口
4. 发布页在审核模式下直接拦截并跳转至首页
5. 详情页的用户评价及评价弹层在审核模式下隐藏,避免 UGC 判定
2026-11-04 16:26:25 +08:00

365 行
9.3 KiB
Vue

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<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>