184 lines
4.4 KiB
Vue
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>
|