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

184 lines
4.4 KiB
Vue

<template>
<view class="my-feedbacks">
<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="feedbacks.length === 0 && !loading" class="empty">暂无反馈记录</view>
<view v-for="f in feedbacks" :key="f.id" class="feedback-card">
<view class="card-header">
<text class="title">{{ f.title }}</text>
<view class="status-tag" :class="'tag-' + f.status">{{ statusLabel(f.status) }}</view>
</view>
<view class="card-meta">
<text>分类:{{ f.category_code }}</text>
<text>提交时间:{{ formatTime(f.created_at) }}</text>
</view>
<view v-if="f.replied_at" class="card-result">
回复时间:{{ formatTime(f.replied_at) }}
</view>
</view>
<view v-if="loading" class="loading">加载中...</view>
<view v-if="noMore && feedbacks.length > 0" class="no-more">没有更多了</view>
</scroll-view>
</view>
</template>
<script>
import { getMyFeedbacksApi } from '@/utils/api.js'
const STATUS_OPTIONS = [
{ value: '', label: '全部' },
{ value: 'pending', label: '待处理' },
{ value: 'reviewing', label: '审核中' },
{ value: 'replied', label: '已回复' },
{ value: 'closed', label: '已关闭' },
{ value: 'archived', label: '已归档' }
]
const STATUS_LABEL_MAP = {
pending: '待处理',
reviewing: '审核中',
replied: '已回复',
closed: '已关闭',
archived: '已归档'
}
export default {
data() {
return {
STATUS_OPTIONS,
feedbacks: [],
currentStatus: '',
page: 1,
pageSize: 20,
loading: false,
noMore: false
}
},
onLoad() {
this.load(true)
},
methods: {
statusLabel(s) { return STATUS_LABEL_MAP[s] || s },
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 getMyFeedbacksApi({ status: this.currentStatus, page, page_size: this.pageSize })
const rows = (resp.data && resp.data.feedbacks) || []
if (reset) {
this.feedbacks = rows
this.page = 1
} else {
this.feedbacks = this.feedbacks.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-feedbacks {
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;
}
.feedback-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;
.title { font-size: 28rpx; font-weight: 600; flex: 1; }
.status-tag {
padding: 6rpx 16rpx;
border-radius: 16rpx;
font-size: 22rpx;
margin-left: 16rpx;
&.tag-pending { background: #f0f0f0; color: #666; }
&.tag-reviewing { background: #fff7e6; color: #fa8c16; }
&.tag-replied { background: #e6f7ff; color: #1890ff; }
&.tag-closed { background: #fafafa; color: #999; }
&.tag-archived { 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: #52c41a;
}
}
</style>