208 lines
5.0 KiB
Vue
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>
|