chuangte_bike_newxcx/page_fenbao/tousu/shlist.vue

596 lines
16 KiB
Vue
Raw Normal View History

2025-08-30 17:38:15 +08:00
<template>
2025-12-20 14:29:10 +08:00
<view class="page">
<u-navbar title="客户反馈管理" :border-bottom="false" :background="bgc" title-color='#000' title-size='36' back-icon-color="#000"
2025-08-30 17:38:15 +08:00
height='44'></u-navbar>
2025-12-20 14:29:10 +08:00
<view class="fixed-header">
<!-- 温馨提示 -->
<view class="warning-tip">
<u-icon name="info-circle" size="28" style="margin-right: 10rpx;"></u-icon>
温馨提示请在24小时内处理订单反馈否则24小时后平台将自动介入
</view>
<!-- 搜索栏 -->
<view class="search-box">
2025-08-30 17:38:15 +08:00
<view class="search-input">
2025-12-20 14:29:10 +08:00
<u-icon name="search" color="#999" size="32"></u-icon>
<input type="text" placeholder="搜索反馈编号/内容" v-model="searchKeyword" confirm-type="search" @confirm="handleSearch" />
</view>
<view class="search-btn" @click="handleSearch">
搜索
2025-08-30 17:38:15 +08:00
</view>
</view>
2025-12-20 14:29:10 +08:00
<!-- 标签页导航 -->
<view class="tabs-box">
<view class="tab-item" :class="{ active: activeTab === 'pending' }" @click="switchTab('pending')">
全部
</view>
<view class="tab-item" :class="{ active: activeTab === 'processed' }" @click="switchTab('processed')">
处理中
</view>
<view class="tab-item" :class="{ active: activeTab === 'rejected' }" @click="switchTab('rejected')">
已处理
</view>
2025-08-30 17:38:15 +08:00
</view>
</view>
2025-12-20 14:29:10 +08:00
<!-- 占位符高度需要根据 fixed-header 的实际高度调整或者使用 padding-top -->
<view class="header-placeholder"></view>
2025-08-30 17:38:15 +08:00
<!-- 反馈列表 -->
2025-12-20 14:29:10 +08:00
<view class="list-container">
<view class="card" v-for="(item, index) in filteredList" :key="index" @click="btnxq(item)">
<view class="card-header">
<view class="title-wrap">
<text class="title">{{item.title || '无标题'}}</text>
</view>
<view class="status-tag" :class="'status-' + item.status">
{{ getStatusText(item.status) }}
2025-08-30 17:38:15 +08:00
</view>
</view>
2025-12-20 14:29:10 +08:00
<view class="divider"></view>
<view class="card-body">
<view class="info-row">
<text class="label">反馈编号</text>
<text class="value">{{item.no}}</text>
</view>
<view class="info-row">
<text class="label">反馈原因</text>
<text class="value content-text">{{item.content}}</text>
</view>
<view class="info-row">
<text class="label">反馈时间</text>
<text class="value">{{item.createTime}}</text>
</view>
<view class="info-row" v-if="item.finishTime">
<text class="label">处理时间</text>
<text class="value">{{item.finishTime}}</text>
</view>
<!-- 剩余时间提示 -->
<view class="expire-box" v-if="item.expireTime && !item.finishTime">
<u-icon name="clock" size="28" :color="getRemainingColor(item.expireTime)"></u-icon>
<text class="expire-text" :style="{color: getRemainingColor(item.expireTime)}">
{{ getRemainingText(item.expireTime) }}
</text>
</view>
2025-08-30 17:38:15 +08:00
</view>
2025-12-20 14:29:10 +08:00
<view class="card-footer">
<text class="detail-link">查看详情</text>
<u-icon name="arrow-right" color="#999" size="24"></u-icon>
2025-08-30 17:38:15 +08:00
</view>
</view>
2025-12-20 14:29:10 +08:00
<view class="loading-text" v-if="list.length > 0">
{{ finished ? '没有更多了' : '加载中...' }}
2025-08-30 17:38:15 +08:00
</view>
2025-12-20 14:29:10 +08:00
<view class="no-data" v-if="list.length === 0 && !loading">
<u-image width="300" height="300" src="https://api.ccttiot.com/smartmeter/img/static/uZFUpcz0YLe0fC7iH0q8" mode="aspectFit"></u-image>
<text>暂无反馈记录</text>
</view>
</view>
2025-08-30 17:38:15 +08:00
</view>
</template>
<script>
export default {
data() {
return {
bgc: {
2025-12-20 14:29:10 +08:00
backgroundColor: "#F7F7F7",
2025-08-30 17:38:15 +08:00
},
activeTab: 'pending',
searchKeyword: '',
pageNum: 1,
pageSize: 20,
total: 0,
list: [],
loading: false,
finished: false,
2025-09-09 11:24:11 +08:00
statusParam: '',
areaId:''
2025-08-30 17:38:15 +08:00
}
},
2025-09-09 11:24:11 +08:00
onLoad(option) {
this.areaId = option.areaId
2025-08-30 17:38:15 +08:00
this.getlist()
},
2025-12-20 14:29:10 +08:00
// 页面触底事件
onReachBottom() {
this.loadMore();
},
2025-08-30 17:38:15 +08:00
computed: {
filteredList() {
2025-12-20 14:29:10 +08:00
// 保持原有的前端搜索逻辑,虽然通常搜索应该走后端,但这里尊重原逻辑
2025-08-30 17:38:15 +08:00
let source = this.list || [];
if (this.searchKeyword) {
const kw = this.searchKeyword.trim().toLowerCase();
source = source.filter(item => {
const text = `${item.no || item.id || ''}${item.title || ''}${item.content || ''}`.toLowerCase();
return text.includes(kw);
});
}
return source;
},
pendingCount() {
return (this.list || []).filter(i => this.normalizeStatus(i.status) === 'pending').length;
}
},
methods: {
2025-12-20 14:29:10 +08:00
getStatusText(status) {
const map = {
1: '商家处理中',
2: '用户处理中', // 假设
3: '平台处理中',
4: '已完成'
};
// 根据实际业务调整,这里沿用原代码的数字逻辑
// 原代码:
// 1: 商家处理中
// 2: 用户处理中
// 3: 平台处理中
// 4: 已完成
return map[status] || '待处理';
},
getRemainingColor(expireTime) {
const text = this.getRemainingText(expireTime);
if (text === '已逾期' || text === '剩余不足1分钟') return '#FF4D4F';
return '#4C97E7';
},
2025-08-30 17:38:15 +08:00
// 计算剩余时间或已逾期
getRemainingText(expireTime){
if(!expireTime){ return '--' }
let expireMs = NaN
if (typeof expireTime === 'number') {
expireMs = expireTime
} else if (typeof expireTime === 'string') {
const str = expireTime.replace(/-/g,'/').replace(/T/,' ').replace(/\.\d{3}Z?$/,'')
expireMs = new Date(str).getTime()
}
if (!expireMs || isNaN(expireMs)) { return '--' }
const now = this.nowTs || Date.now()
let diff = expireMs - now
if (diff <= 0) { return '已逾期' }
const minute = 60000
const hour = 60 * minute
const day = 24 * hour
const d = Math.floor(diff / day); diff %= day
const h = Math.floor(diff / hour); diff %= hour
const m = Math.floor(diff / minute)
if (d > 0) { return `剩余${d}${h}小时` }
if (h > 0) { return `剩余${h}小时${m}分钟` }
if (m > 0) { return `剩余${m}分钟` }
return '剩余不足1分钟'
},
// 切换tab
switchTab(tab) {
2025-12-20 14:29:10 +08:00
if (this.activeTab === tab) return;
2025-08-30 17:38:15 +08:00
this.activeTab = tab;
if (tab === 'pending') {
this.statusParam = '';
} else if (tab === 'processed') {
this.statusParam = '1,3';
} else if (tab === 'rejected') {
2025-12-20 14:29:10 +08:00
this.statusParam = '2,4'; // 原代码逻辑rejected 对应 2,4 (已处理/已拒绝?)
2025-08-30 17:38:15 +08:00
} else {
this.statusParam = '';
}
this.pageNum = 1;
this.list = [];
this.finished = false;
this.getlist();
},
// 点击跳转到商户投诉详情
btnxq(item){
console.log(item);
uni.navigateTo({
url:'/page_fenbao/tousu/shtsxq?id=' + item.id
})
},
// 正常化状态
normalizeStatus(status) {
if (status === undefined || status === null) return 'pending';
const s = String(status).toLowerCase();
if (['0', 'pending', 'wait', 'waiting', '未处理'].includes(s)) return 'pending';
if (['1', 'processed', 'done', '已处理', '已完成'].includes(s)) return 'processed';
if (['2', 'rejected', 'refused', '已拒绝'].includes(s)) return 'rejected';
return 'pending';
},
// 搜索
handleSearch() {
this.pageNum = 1;
this.list = [];
this.finished = false;
this.getlist();
},
// 触底加载
loadMore() {
if (this.loading || this.finished) return;
this.pageNum += 1;
this.getlist();
},
// 请求投诉列表
getlist() {
if (this.loading) return;
this.loading = true;
2025-09-09 11:24:11 +08:00
const base = `/bst/complaint/list?pageNum=${this.pageNum}&pageSize=${this.pageSize}&orderByColumn=createTime&isAsc=desc&areaId=${this.areaId}`;
2025-08-30 17:38:15 +08:00
const url = this.statusParam ? `${base}&statusList=${encodeURIComponent(this.statusParam)}` : base;
this.$u.get(url).then((res) => {
if (!res) return;
// 优先解析顶层 rows/total
if (Array.isArray(res.rows)) {
const rows = res.rows;
const total = res.total || res.totalCount || res.count || rows.length || 0;
const mapped = (rows || []).map(r => ({
id: r.id || r.complaintId || r.feedbackId || r.no,
no: r.no || r.feedbackId || r.complaintId || r.id,
title: r.title || r.reason || r.typeName || '',
content: r.content || r.reason || '',
createTime: r.createTime || r.create_time || r.createdAt || r.create_at || r.applyTime,
finishTime: r.finishTime || r.processTime || r.process_time || r.updatedAt || r.updateTime,
expireTime:r.expireTime,
status: r.status
}));
this.total = total;
this.list = this.pageNum === 1 ? mapped : this.list.concat(mapped);
if (this.list.length >= this.total || mapped.length < this.pageSize) {
this.finished = true;
}
return;
}
2025-12-20 14:29:10 +08:00
// 兼容原有逻辑...
// 顶层 rows 为对象场景
2025-08-30 17:38:15 +08:00
if (res.rows && typeof res.rows === 'object' && !Array.isArray(res.rows)) {
let rows = res.rows.list || res.rows.rows || res.rows.records || res.rows.items || res.rows.data || [];
const total = res.total || res.totalCount || res.rows.total || res.rows.totalCount || rows.length || 0;
if (!Array.isArray(rows)) rows = [];
const mapped = (rows || []).map(r => ({
id: r.id || r.complaintId || r.feedbackId || r.no,
no: r.no || r.feedbackId || r.complaintId || r.id,
title: r.title || r.reason || r.typeName || '',
content: r.content || r.reason || '',
createTime: r.createTime || r.create_time || r.createdAt || r.create_at || r.applyTime,
finishTime: r.finishTime || r.processTime || r.process_time || r.updatedAt || r.updateTime,
status: r.status
}));
this.total = total;
this.list = this.pageNum === 1 ? mapped : this.list.concat(mapped);
if (this.list.length >= this.total || mapped.length < this.pageSize) {
this.finished = true;
}
return;
}
2025-12-20 14:29:10 +08:00
// 其他情况
2025-08-30 17:38:15 +08:00
const data = res.data !== undefined ? res.data : (res.result !== undefined ? res.result : (res.body !== undefined ? res.body : res));
let total = 0;
if (data && typeof data === 'object' && !Array.isArray(data)) {
total = data.total || data.totalCount || data.totalRow || res.total || res.count || 0;
}
let rows = [];
if (Array.isArray(data)) {
rows = data;
if (!total) total = rows.length;
} else if (data && typeof data === 'object') {
rows = data.list || data.rows || data.records || data.items || data.data || [];
if (!Array.isArray(rows)) {
rows = res.list || res.rows || res.records || res.items || [];
if (!Array.isArray(rows)) {
Object.keys(data).forEach(k => { if (Array.isArray(data[k])) rows = data[k]; });
}
}
if (!total) total = data.total || data.totalCount || data.totalRow || rows.length || 0;
}
const mapped = (rows || []).map(r => ({
id: r.id || r.complaintId || r.feedbackId || r.no,
no: r.no || r.feedbackId || r.complaintId || r.id,
title: r.title || r.reason || r.typeName || '',
content: r.content || r.reason || '',
createTime: r.createTime || r.create_time || r.createdAt || r.create_at || r.applyTime,
finishTime: r.finishTime || r.processTime || r.process_time || r.updatedAt || r.updateTime,
status: r.status
}));
this.total = total;
this.list = this.pageNum === 1 ? mapped : this.list.concat(mapped);
if (this.list.length >= this.total || mapped.length < this.pageSize) {
this.finished = true;
}
}).finally(() => {
this.loading = false;
});
}
}
}
</script>
2025-12-20 14:29:10 +08:00
<style lang="scss" scoped>
2025-08-30 17:38:15 +08:00
page {
2025-12-20 14:29:10 +08:00
background-color: #F7F7F7;
2025-08-30 17:38:15 +08:00
}
2025-12-20 14:29:10 +08:00
.page {
2025-08-30 17:38:15 +08:00
min-height: 100vh;
2025-12-20 14:29:10 +08:00
background-color: #F7F7F7;
padding-bottom: 40rpx;
2025-08-30 17:38:15 +08:00
}
2025-12-20 14:29:10 +08:00
.fixed-header {
position: fixed;
top: 44px; /* 假设 navbar 高度为 44px */
/* #ifdef H5 */
top: 88rpx;
/* #endif */
left: 0;
width: 100%;
z-index: 100;
2025-08-30 17:38:15 +08:00
background-color: #fff;
2025-12-20 14:29:10 +08:00
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.05);
}
.header-placeholder {
height: 200rpx; /* 根据 fixed-header 高度估算 */
}
.warning-tip {
background-color: #FFFBE6;
2025-08-30 17:38:15 +08:00
padding: 16rpx 24rpx;
2025-12-20 14:29:10 +08:00
color: #E6A23C;
2025-08-30 17:38:15 +08:00
font-size: 24rpx;
2025-12-20 14:29:10 +08:00
display: flex;
align-items: center;
2025-08-30 17:38:15 +08:00
}
2025-12-20 14:29:10 +08:00
.search-box {
padding: 20rpx 30rpx;
display: flex;
align-items: center;
background: #fff;
.search-input {
flex: 1;
height: 72rpx;
background: #F5F7FA;
border-radius: 36rpx;
2025-08-30 17:38:15 +08:00
display: flex;
align-items: center;
2025-12-20 14:29:10 +08:00
padding: 0 30rpx;
margin-right: 20rpx;
input {
2025-08-30 17:38:15 +08:00
flex: 1;
2025-12-20 14:29:10 +08:00
font-size: 28rpx;
color: #333;
margin-left: 10rpx;
2025-08-30 17:38:15 +08:00
}
2025-12-20 14:29:10 +08:00
}
.search-btn {
width: 120rpx;
height: 72rpx;
background: linear-gradient(90deg, #4C97E7, #6ab0ff);
border-radius: 36rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 28rpx;
font-weight: 500;
&:active {
opacity: 0.9;
2025-08-30 17:38:15 +08:00
}
}
}
2025-12-20 14:29:10 +08:00
.tabs-box {
2025-08-30 17:38:15 +08:00
display: flex;
2025-12-20 14:29:10 +08:00
background: #fff;
padding: 0 10rpx;
border-bottom: 1rpx solid #f5f5f5;
2025-08-30 17:38:15 +08:00
.tab-item {
flex: 1;
text-align: center;
2025-12-20 14:29:10 +08:00
padding: 24rpx 0;
2025-08-30 17:38:15 +08:00
font-size: 28rpx;
2025-12-20 14:29:10 +08:00
color: #666;
2025-08-30 17:38:15 +08:00
position: relative;
2025-12-20 14:29:10 +08:00
transition: all 0.3s;
&.active {
color: #4C97E7;
font-weight: 600;
font-size: 30rpx;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background: #4C97E7;
border-radius: 4rpx;
}
2025-08-30 17:38:15 +08:00
}
}
}
2025-12-20 14:29:10 +08:00
.list-container {
padding: 24rpx;
}
.card {
background: #FFFFFF;
border-radius: 20rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.03);
overflow: hidden;
transition: all 0.3s;
&:active {
transform: scale(0.99);
}
.card-header {
padding: 24rpx 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
.title-wrap {
flex: 1;
margin-right: 20rpx;
.title {
font-size: 30rpx;
font-weight: 600;
color: #333;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
}
.status-tag {
font-size: 22rpx;
padding: 4rpx 12rpx;
border-radius: 8rpx;
white-space: nowrap;
&.status-1 { // 商家处理中
background: #EBF4FF;
color: #4C97E7;
}
&.status-2 { // 用户处理中
background: #FFF0E6;
color: #FF8C00;
}
&.status-3 { // 平台处理中
background: #F2EBFF;
color: #7B2BF9;
}
&.status-4 { // 已完成
background: #F0F9EB;
color: #67C23A;
}
}
}
.divider {
height: 1rpx;
background: #f9f9f9;
margin: 0 30rpx;
}
.card-body {
padding: 24rpx 30rpx;
.info-row {
display: flex;
margin-bottom: 16rpx;
font-size: 26rpx;
line-height: 1.5;
&:last-child {
margin-bottom: 0;
}
.label {
color: #999;
width: 140rpx;
flex-shrink: 0;
}
.value {
color: #333;
flex: 1;
&.content-text {
color: #666;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
}
}
.expire-box {
margin-top: 20rpx;
background: #FFFBE6;
padding: 12rpx 20rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
.expire-text {
font-size: 24rpx;
margin-left: 10rpx;
font-weight: 500;
}
}
}
.card-footer {
border-top: 1rpx solid #f9f9f9;
padding: 20rpx 30rpx;
display: flex;
justify-content: flex-end;
align-items: center;
.detail-link {
font-size: 24rpx;
color: #999;
margin-right: 6rpx;
}
}
}
.loading-text {
text-align: center;
color: #999;
font-size: 24rpx;
padding: 30rpx 0;
}
.no-data {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 100rpx;
text {
font-size: 28rpx;
color: #999;
margin-top: 20rpx;
}
2025-08-30 17:38:15 +08:00
}
</style>