topfans/frontend/pages/profile/myReports.vue
2026-06-22 17:19:48 +08:00

208 lines
5.0 KiB
Vue

<template>
<view class="my-reports">
<view class="filter-bar">
<view
v-for="s in STATUS_OPTIONS"
:key="s.value"
class="filter-tab"
:class="{ active: currentStatus === s.value }"
@click="switchStatus(s.value)"
>
{{ s.label }}
</view>
</view>
<scroll-view scroll-y="true" class="list" @scrolltolower="loadMore">
<view v-if="reports.length === 0 && !loading" class="empty">暂无举报记录</view>
<view v-for="r in reports" :key="r.id" class="report-card">
<view class="card-header">
<text class="target-label">{{ r.target_type === 'asset' ? '藏品' : '用户' }} #{{ r.target_id }}</text>
<view class="status-tag" :class="'tag-' + r.status">{{ statusLabel(r.status) }}</view>
</view>
<view class="card-meta">
<text>分类:{{ r.category_code }}</text>
<text>提交时间:{{ formatTime(r.created_at) }}</text>
</view>
<view v-if="r.resolved_action" class="card-result">
处理结果:<text class="result-text">{{ resultLabel(r.resolved_action) }}</text>
<text v-if="r.resolved_at"> · {{ formatTime(r.resolved_at) }}</text>
</view>
</view>
<view v-if="loading" class="loading">加载中...</view>
<view v-if="noMore && reports.length > 0" class="no-more">没有更多了</view>
</scroll-view>
</view>
</template>
<script>
import { getMyReportsApi } from '@/utils/api.js'
const STATUS_OPTIONS = [
{ value: '', label: '全部' },
{ value: 'pending', label: '待处理' },
{ value: 'reviewing', label: '审核中' },
{ value: 'auto_hidden', label: '已自动隐藏' },
{ value: 'resolved', label: '已处理' },
{ value: 'dismissed', label: '已驳回' },
{ value: 'withdrawn', label: '已撤回' }
]
const STATUS_LABEL_MAP = {
pending: '待处理',
reviewing: '审核中',
auto_hidden: '已自动隐藏',
resolved: '已处理',
dismissed: '已驳回',
withdrawn: '已撤回'
}
const RESULT_LABEL_MAP = {
takedown: '已下架',
ban: '已封禁',
warn: '已警告',
dismiss: '已驳回',
restore: '已解除'
}
export default {
data() {
return {
STATUS_OPTIONS,
reports: [],
currentStatus: '',
page: 1,
pageSize: 20,
loading: false,
noMore: false,
total: 0
}
},
onLoad() {
this.load(true)
},
methods: {
statusLabel(s) { return STATUS_LABEL_MAP[s] || s },
resultLabel(a) { return RESULT_LABEL_MAP[a] || a },
formatTime(ms) {
if (!ms) return ''
const d = new Date(ms)
const pad = n => (n < 10 ? '0' + n : '' + n)
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
},
switchStatus(s) {
if (this.currentStatus === s) return
this.currentStatus = s
this.load(true)
},
async load(reset) {
if (this.loading) return
this.loading = true
try {
const page = reset ? 1 : this.page
const resp = await getMyReportsApi({ status: this.currentStatus, page, page_size: this.pageSize })
const rows = (resp.data && resp.data.reports) || []
this.total = (resp.data && resp.data.total) || 0
if (reset) {
this.reports = rows
this.page = 1
} else {
this.reports = this.reports.concat(rows)
}
if (rows.length < this.pageSize) {
this.noMore = true
} else {
this.noMore = false
}
} catch (e) {
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
this.loading = false
}
},
loadMore() {
if (this.noMore || this.loading) return
this.page++
this.load(false)
}
}
}
</script>
<style lang="scss" scoped>
.my-reports {
display: flex;
flex-direction: column;
height: 100vh;
background: #f7f8fa;
}
.filter-bar {
display: flex;
overflow-x: auto;
white-space: nowrap;
background: #fff;
border-bottom: 1rpx solid #eee;
padding: 16rpx 24rpx;
.filter-tab {
display: inline-block;
padding: 12rpx 24rpx;
font-size: 26rpx;
color: #666;
border-radius: 32rpx;
margin-right: 16rpx;
&.active {
background: #5C40FF;
color: #fff;
}
}
}
.list {
flex: 1;
padding: 24rpx;
}
.empty, .loading, .no-more {
text-align: center;
color: #999;
padding: 48rpx 0;
font-size: 26rpx;
}
.report-card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 16rpx;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
.target-label { font-size: 28rpx; font-weight: 600; }
.status-tag {
padding: 6rpx 16rpx;
border-radius: 16rpx;
font-size: 22rpx;
&.tag-pending { background: #f0f0f0; color: #666; }
&.tag-reviewing { background: #fff7e6; color: #fa8c16; }
&.tag-auto_hidden { background: #ffe7e7; color: #f5222d; }
&.tag-resolved { background: #e6f7ff; color: #1890ff; }
&.tag-dismissed { background: #fafafa; color: #999; }
&.tag-withdrawn { background: #fafafa; color: #999; }
}
}
.card-meta {
display: flex;
flex-direction: column;
gap: 6rpx;
font-size: 24rpx;
color: #666;
}
.card-result {
margin-top: 12rpx;
padding-top: 12rpx;
border-top: 1rpx solid #f5f5f5;
font-size: 24rpx;
color: #666;
.result-text { color: #5C40FF; font-weight: 600; }
}
}
</style>