You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
crmeb/app/pages/supply_chain/complaint/index.vue

565 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="complaint-page">
<view class="header">
<view class="back" @click="goBack">
<text class="iconfont icon-xiangzuo"></text>
</view>
<view class="title">投诉与建议</view>
<view class="right"></view>
</view>
<view class="content">
<!-- 顶部标签我要反馈 / 我的记录 -->
<view class="tabs">
<view
class="tab"
:class="activeTab === 'form' ? 'active' : ''"
@click="activeTab = 'form'"
>
我要反馈
</view>
<view
class="tab"
:class="activeTab === 'list' ? 'active' : ''"
@click="switchToList"
>
我的记录
</view>
</view>
<!-- 表单区域 -->
<view class="tab-content" v-show="activeTab === 'form'">
<view class="form-card">
<view class="form-item">
<text class="label">反馈类型</text>
<view class="type-tags">
<view
class="type-tag"
:class="form.csType === '投诉' ? 'checked' : ''"
@click="form.csType = '投诉'"
>
投诉
</view>
<view
class="type-tag"
:class="form.csType === '建议' ? 'checked' : ''"
@click="form.csType = '建议'"
>
建议
</view>
</view>
</view>
<view class="form-item">
<text class="label">联系方式</text>
<input
class="input"
type="text"
v-model="form.phone"
placeholder="请输入手机号(方便回访)"
maxlength="20"
/>
</view>
<view class="form-item">
<text class="label">反馈内容</text>
<textarea
class="textarea"
v-model="form.csContent"
placeholder="请详细描述您的问题或建议..."
maxlength="300"
:auto-height="true"
/>
<text class="count">{{ form.csContent.length }}/300</text>
</view>
<view class="form-item remark-item">
<text class="label">备注(选填)</text>
<input
class="input"
type="text"
v-model="form.remark"
placeholder="如有补充说明可在此填写"
maxlength="100"
/>
</view>
</view>
<view class="submit-section">
<view class="submit-btn" @click="submitComplaint">
提交
</view>
</view>
</view>
<!-- 列表区域 -->
<view class="tab-content" v-show="activeTab === 'list'">
<scroll-view
scroll-y
class="list-scroll"
@scrolltolower="loadMore"
>
<view
class="record-card"
v-for="item in records"
:key="item.id"
>
<view class="card-header">
<view class="left">
<text class="type">{{ item.csType || '—' }}</text>
<text
class="status"
:class="statusClass(item.status)"
>
{{ statusText(item.status) }}
</text>
</view>
<text class="time">
{{ item.submitTime || item.createTime || '' }}
</text>
</view>
<view class="card-body">
<view class="row">
<text class="label">内容</text>
<text class="value">
{{ item.csContent || '—' }}
</text>
</view>
<view class="row" v-if="item.handlerReslut">
<text class="label">处理结果</text>
<text class="value">
{{ item.handlerReslut }}
</text>
</view>
<view class="row" v-if="item.handlerName">
<text class="label">处理人</text>
<text class="value">
{{ item.handlerName }}
</text>
</view>
<view class="row" v-if="item.handlerDate">
<text class="label">处理时间</text>
<text class="value">
{{ item.handlerDate }}
</text>
</view>
</view>
</view>
<view class="empty" v-if="!loading && records.length === 0">
<text class="iconfont icon-wushuju"></text>
<text class="text">暂无反馈记录</text>
</view>
<view class="load-more" v-if="loading">
加载中...
</view>
<view
class="load-more"
v-else-if="finished && records.length > 0"
>
已加载全部
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
import {
createComplaintSuggestion,
listComplaintSuggestion
} from '@/api/property.js';
export default {
data() {
return {
activeTab: 'form',
form: {
csType: '投诉',
csContent: '',
remark: '',
phone: ''
},
records: [],
page: 1,
limit: 10,
loading: false,
finished: false
};
},
onShow() {
if (this.activeTab === 'list') {
this.refreshList();
}
},
methods: {
goBack() {
uni.navigateBack();
},
switchToList() {
this.activeTab = 'list';
if (!this.records.length) {
this.refreshList();
}
},
async submitComplaint() {
if (!this.form.csContent.trim()) {
uni.showToast({
title: '请填写反馈内容',
icon: 'none'
});
return;
}
const payload = {
csType: this.form.csType,
csContent: this.form.csContent.trim(),
remark: this.form.remark.trim(),
phone: this.form.phone.trim(),
submitChannel: 'APP'
};
try {
uni.showLoading({ title: '提交中...', mask: true });
await createComplaintSuggestion(payload);
uni.hideLoading();
uni.showToast({
title: '提交成功',
icon: 'success'
});
this.form.csContent = '';
this.form.remark = '';
this.page = 1;
this.records = [];
this.finished = false;
if (this.activeTab === 'list') {
this.refreshList();
}
} catch (e) {
uni.hideLoading();
uni.showToast({
title: typeof e === 'string' ? e : '提交失败,请稍后重试',
icon: 'none'
});
}
},
async refreshList() {
this.page = 1;
this.records = [];
this.finished = false;
await this.fetchList();
},
async loadMore() {
if (this.loading || this.finished) return;
this.page += 1;
await this.fetchList();
},
async fetchList() {
this.loading = true;
try {
const params = {
page: this.page,
limit: this.limit
};
const res = await listComplaintSuggestion(params);
const list = (res && (res.list || res.data || res.rows)) || [];
if (this.page === 1) {
this.records = list;
} else {
this.records = this.records.concat(list);
}
if (!list.length || (res && (res.total || res.count)) <= this.records.length) {
this.finished = true;
}
} catch (e) {
uni.showToast({
title: typeof e === 'string' ? e : '获取记录失败',
icon: 'none'
});
} finally {
this.loading = false;
}
},
statusText(status) {
if (!status) return '待处理';
if (status === '1' || status === '已处理') return '已处理';
if (status === '0' || status === '待处理') return '待处理';
return status;
},
statusClass(status) {
const text = this.statusText(status);
if (text === '已处理') return 'status-done';
if (text === '待处理') return 'status-pending';
return '';
}
}
};
</script>
<style lang="scss">
.complaint-page {
.header {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
padding: 0 30rpx;
background-color: #409EFF;
color: #fff;
.back {
width: 60rpx;
height: 100%;
display: flex;
align-items: center;
.iconfont {
font-size: 32rpx;
color: #fff;
}
}
.title {
font-size: 32rpx;
font-weight: 600;
}
.right {
width: 60rpx;
}
}
.content {
padding: 30rpx;
.tabs {
display: flex;
margin-bottom: 30rpx;
background-color: #fff;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
.tab {
flex: 1;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
color: #666;
position: relative;
&.active {
color: #409EFF;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 4rpx;
background-color: #409EFF;
border-radius: 2rpx;
}
}
}
}
.tab-content {
.form-card {
background-color: #fff;
border-radius: 10rpx;
padding: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.08);
.form-item {
margin-bottom: 30rpx;
.label {
display: block;
font-size: 26rpx;
color: #333;
margin-bottom: 16rpx;
font-weight: 600;
}
.input {
width: 100%;
height: 72rpx;
border-radius: 10rpx;
border: 1rpx solid #e5e5e5;
padding: 0 20rpx;
font-size: 24rpx;
box-sizing: border-box;
}
.textarea {
width: 100%;
min-height: 160rpx;
border-radius: 10rpx;
border: 1rpx solid #e5e5e5;
padding: 20rpx;
font-size: 24rpx;
box-sizing: border-box;
line-height: 1.6;
}
.count {
display: block;
margin-top: 10rpx;
text-align: right;
font-size: 20rpx;
color: #999;
}
.type-tags {
display: flex;
.type-tag {
min-width: 140rpx;
padding: 14rpx 24rpx;
border-radius: 40rpx;
border: 1rpx solid #e5e5e5;
font-size: 24rpx;
color: #666;
text-align: center;
margin-right: 20rpx;
&.checked {
border-color: #409EFF;
background-color: #E6F7FF;
color: #409EFF;
}
}
}
}
.remark-item {
margin-bottom: 0;
}
}
.submit-section {
margin-top: 40rpx;
.submit-btn {
height: 88rpx;
background-color: #409EFF;
color: #fff;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
font-weight: 600;
box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.35);
}
}
.list-scroll {
max-height: calc(100vh - 200rpx);
}
.record-card {
background-color: #fff;
border-radius: 10rpx;
padding: 24rpx 26rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.06);
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
.left {
display: flex;
align-items: center;
.type {
font-size: 26rpx;
font-weight: 600;
color: #333;
margin-right: 16rpx;
}
.status {
padding: 6rpx 18rpx;
border-radius: 20rpx;
font-size: 20rpx;
}
.status-pending {
background-color: #E6F7FF;
color: #409EFF;
}
.status-done {
background-color: #F6FFED;
color: #52C41A;
}
}
.time {
font-size: 22rpx;
color: #999;
}
}
.card-body {
.row {
display: flex;
margin-bottom: 10rpx;
.label {
width: 140rpx;
font-size: 24rpx;
color: #666;
}
.value {
flex: 1;
font-size: 24rpx;
color: #333;
}
}
}
}
.empty {
margin-top: 80rpx;
display: flex;
flex-direction: column;
align-items: center;
color: #999;
.iconfont {
font-size: 80rpx;
margin-bottom: 16rpx;
}
.text {
font-size: 24rpx;
}
}
.load-more {
text-align: center;
padding: 20rpx 0 10rpx;
font-size: 22rpx;
color: #999;
}
}
}
}
</style>