Files
uni-fans-score/pages/feedback/list.vue
T

494 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="feedback-list-container">
<!-- 状态切换 -->
<view class="status-tabs">
<view v-for="(tab, index) in statusTabs" :key="index" class="tab-item"
:class="{ active: currentTab === index }" @click="switchTab(index)">
{{ tab.text }}
</view>
</view>
<!-- 投诉列表 -->
<scroll-view class="feedback-list" scroll-y @scrolltolower="loadMore" :refresher-enabled="true"
:refresher-triggered="refreshing" @refresherrefresh="onRefresh">
<view class="empty-state" v-if="feedbackList.length === 0 && !loading">
<view class="empty-icon">
<image src="/static/suggess.png" mode="aspectFill" class="empty-icon"></image>
</view>
<text class="empty-text">{{ $t('feedback.noRecord') }}</text>
</view>
<view class="feedback-item" v-for="(item, index) in feedbackList" :key="index"
@click="navigateToDetail(item)">
<view class="item-header">
<view class="header-left">
<view class="status-chip" :class="getStatusClass(item.status)">
{{ getStatusText(item.status) }}
</view>
<view class="type-text">{{ getTypeText(item.type) }}</view>
</view>
<view class="header-right">
<text class="time-text">{{ formatTime(item.createTime) }}</text>
</view>
</view>
<view class="item-content">
<view class="content-text">{{ item.content || '-' }}</view>
<view class="content-images" v-if="getImageList(item).length > 0">
<image v-for="(img, imgIndex) in getImageList(item)" :key="imgIndex" :src="img" mode="aspectFill" class="content-image"></image>
</view>
</view>
<view class="item-footer">
<view class="footer-left">
<text class="phone-text">{{ $t('feedback.contactPhone') }}{{ item.phone || '-' }}</text>
</view>
<view class="footer-right">
<uv-icon name="arrow-right" size="16" color="#999"></uv-icon>
</view>
</view>
</view>
<!-- 加载更多 -->
<view class="load-more" v-if="hasMore && feedbackList.length > 0">
<text class="load-more-text">{{ $t('common.loading') }}</text>
</view>
<view class="no-more" v-if="!hasMore && feedbackList.length > 0">
<text class="no-more-text">{{ $t('common.noMore') }}</text>
</view>
</scroll-view>
</view>
</template>
<script setup>
import {
ref,
reactive,
onMounted
} from 'vue';
import {
onLoad,
onShow
} from '@dcloudio/uni-app';
import {
getFeedbackList
} from '../../config/api/feedback.js';
import {
useI18n
} from '@/utils/i18n.js'
const {
t: $t
} = useI18n()
// 设置页面标题
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('feedback.recordList')
})
})
// 初始化状态
const currentTab = ref(0);
const feedbackList = ref([]);
const loading = ref(false);
const refreshing = ref(false);
const hasMore = ref(true);
const currentPage = ref(1);
const pageSize = ref(10);
// 状态标签
const statusTabs = reactive([{
get text() {
return $t('common.all')
},
status: ''
},
{
get text() {
return $t('feedback.pending')
},
status: 'pending'
},
{
get text() {
return $t('feedback.processing')
},
status: 'in_progress'
},
{
get text() {
return $t('feedback.completed')
},
status: 'resolved'
}
]);
// 页面加载
onLoad(async () => {
await loadFeedbackList();
});
// 页面显示时刷新
onShow(async () => {
// 可以在这里刷新列表
});
// 切换标签
const switchTab = async (index) => {
currentTab.value = index;
currentPage.value = 1;
feedbackList.value = [];
hasMore.value = true;
await loadFeedbackList();
};
// 加载投诉列表
const loadFeedbackList = async (isLoadMore = false) => {
if (loading.value) return;
try {
loading.value = true;
const status = statusTabs[currentTab.value].status;
const params = {
pageNum: currentPage.value,
pageSize: pageSize.value
};
if (status) {
params.status = status;
}
const res = await getFeedbackList(params);
if (res.code === 200 && res.data) {
const records = res.data.records || res.data.list || [];
if (isLoadMore) {
feedbackList.value = [...feedbackList.value, ...records];
} else {
feedbackList.value = records;
}
// 判断是否还有更多数据
const total = res.data.total || 0;
hasMore.value = feedbackList.value.length < total;
if (hasMore.value) {
currentPage.value += 1;
}
} else {
uni.showToast({
title: res.msg || $t('feedback.getListFailed'),
icon: 'none'
});
}
} catch (error) {
console.error('获取投诉列表失败:', error);
uni.showToast({
title: $t('feedback.getListFailed'),
icon: 'none'
});
} finally {
loading.value = false;
refreshing.value = false;
}
};
// 加载更多
const loadMore = () => {
if (hasMore.value && !loading.value) {
loadFeedbackList(true);
}
};
// 下拉刷新
const onRefresh = () => {
refreshing.value = true;
currentPage.value = 1;
feedbackList.value = [];
hasMore.value = true;
loadFeedbackList();
};
// 获取状态文本
const getStatusText = (status) => {
const statusMap = {
'pending': $t('feedback.pending'),
'in_progress': $t('feedback.processing'),
'resolved': $t('feedback.completed')
};
return statusMap[status] || $t('feedback.pending');
};
// 获取状态样式类
const getStatusClass = (status) => {
const classMap = {
'pending': 'chip-pending',
'in_progress': 'chip-processing',
'resolved': 'chip-completed'
};
return classMap[status] || 'chip-pending';
};
// 获取类型文本
const getTypeText = (type) => {
const typeMap = {
'complain': $t('feedback.complain'),
'suggestion': $t('feedback.suggestion')
};
return typeMap[type] || type || '-';
};
// 格式化时间
const formatTime = (timeStr) => {
if (!timeStr) return '-';
try {
const date = new Date(timeStr);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
} catch (e) {
return timeStr;
}
};
// 获取图片列表(支持字符串或数组)
const getImageList = (item) => {
if (!item.picturePath) return [];
if (Array.isArray(item.picturePath)) return item.picturePath;
if (typeof item.picturePath === 'string') {
// 如果是逗号分隔的字符串,拆分为数组
if (item.picturePath.includes(',')) {
return item.picturePath.split(',').filter(img => img.trim());
}
return [item.picturePath];
}
return [];
};
// 跳转到详情页
const navigateToDetail = (item) => {
uni.navigateTo({
url: `/pages/feedback/detail?id=${item.id || item.feedbackId}`
});
};
</script>
<style lang="scss" scoped>
.feedback-list-container {
min-height: 100vh;
background: #f7f8fa;
padding-bottom: 30rpx;
// 状态标签栏
.status-tabs {
display: flex;
background: #fff;
padding: 0 20rpx;
position: sticky;
top: 0;
z-index: 10;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.tab-item {
flex: 1;
height: 90rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #666;
position: relative;
&.active {
color: #07c160;
font-weight: 500;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background: #07c160;
border-radius: 2rpx;
}
}
}
}
// 投诉列表
.feedback-list {
height: calc(100vh - 90rpx);
padding: 20rpx;
box-sizing: border-box;
// 投诉项
.feedback-item {
width: 100%;
box-sizing: border-box;
background: #fff;
border-radius: 16rpx;
margin-bottom: 20rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
// 头部
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
border-bottom: 1rpx solid #f0f0f0;
.header-left {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
margin-right: 16rpx;
.status-chip {
padding: 6rpx 16rpx;
border-radius: 8rpx;
font-size: 24rpx;
margin-right: 16rpx;
flex-shrink: 0;
&.chip-processing {
background: rgba(255, 152, 0, 0.12);
color: #FF9800;
}
&.chip-completed {
background: rgba(76, 175, 80, 0.12);
color: #4CAF50;
}
&.chip-pending {
background: rgba(158, 158, 158, 0.12);
color: #9E9E9E;
}
}
.type-text {
font-size: 28rpx;
color: #333;
font-weight: 500;
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.header-right {
flex-shrink: 0;
.time-text {
font-size: 24rpx;
color: #999;
white-space: nowrap;
}
}
}
// 内容
.item-content {
padding: 24rpx;
.content-text {
font-size: 28rpx;
color: #333;
line-height: 1.6;
margin-bottom: 16rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
}
.content-images {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
.content-image {
width: 160rpx;
height: 160rpx;
border-radius: 8rpx;
background: #f5f5f5;
}
}
}
// 底部
.item-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 24rpx;
background: #fafafa;
border-top: 1rpx solid #f0f0f0;
.footer-left {
flex: 1;
min-width: 0;
margin-right: 16rpx;
.phone-text {
font-size: 24rpx;
color: #999;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.footer-right {
display: flex;
align-items: center;
flex-shrink: 0;
}
}
}
// 空状态
.empty-state {
padding: 100rpx 0;
text-align: center;
.empty-icon {
width: 180rpx;
height: 180rpx;
margin: 0 auto 30rpx;
opacity: 0.5;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
}
// 加载更多
.load-more,
.no-more {
padding: 30rpx 0;
text-align: center;
.load-more-text,
.no-more-text {
font-size: 24rpx;
color: #999;
}
}
}
}
</style>