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.
455 lines
11 KiB
455 lines
11 KiB
|
3 weeks ago
|
<template>
|
||
|
|
<view class="page">
|
||
|
|
<view class="title">
|
||
|
|
<view class="title-sreach">
|
||
|
|
<view class="back-btn" @tap="back" :style="{'top': menuButtonInfo.top +'px'}">
|
||
|
|
<uni-icons type="left" size="28"></uni-icons>
|
||
|
|
</view>
|
||
|
|
<view class="title-name" :style="{'padding-top': menuButtonInfo.top +'px'}">
|
||
|
|
投诉吐槽
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
<view class="content">
|
||
|
|
<view class="add-card">
|
||
|
|
<view class="card-title">我要吐槽</view>
|
||
|
|
<picker :range="typeOptions" @change="changeType">
|
||
|
|
<view class="type-picker">{{form.feedbackType || '请选择类型'}} <text>›</text></view>
|
||
|
|
</picker>
|
||
|
|
<textarea class="feedback-textarea" maxlength="300" v-model="form.problemContent"
|
||
|
|
placeholder="遇到了什么问题?写下来我们会尽快处理"></textarea>
|
||
|
|
<input class="phone-input" v-model="form.contactPhone" placeholder="联系电话(选填,方便我们联系你)" />
|
||
|
|
<view class="upload-row">
|
||
|
|
<view class="upload-btn" @tap="chooseImage">
|
||
|
|
<uni-icons type="camera" size="28" color="#777"></uni-icons>
|
||
|
|
</view>
|
||
|
|
<view class="upload-preview" v-for="(pic,index) in pictureList" :key="index">
|
||
|
|
<image :src="pic" mode="aspectFill" @tap="previewImage(index)"></image>
|
||
|
|
<text class="remove-img" @tap.stop="removeImage(index)">×</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
<view class="submit-btn" @tap="submitFeedback">提交</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<view class="list-head">
|
||
|
|
<text>我的反馈</text>
|
||
|
|
<text class="list-tip">按处理状态和时间排序</text>
|
||
|
|
</view>
|
||
|
|
<scroll-view scroll-y class="feedback-scroll" @scrolltolower="loadMore">
|
||
|
|
<view class="feedback-item" v-for="(item,index) in feedbackList" :key="index">
|
||
|
|
<view class="item-head">
|
||
|
|
<text class="item-type">{{item.feedbackType || '投诉吐槽'}}</text>
|
||
|
|
<text :class="item.status == 1 ? 'status done' : 'status'">{{item.status == 1 ? '已回复' : '待回复'}}</text>
|
||
|
|
</view>
|
||
|
|
<view class="item-content">{{item.problemContent}}</view>
|
||
|
|
<view class="item-pictures" v-if="getPictureList(item.problemPicture).length">
|
||
|
|
<image v-for="(pic,picIndex) in getPictureList(item.problemPicture)" :key="picIndex"
|
||
|
|
:src="pic" mode="aspectFill" @tap="previewItemImage(item.problemPicture, picIndex)"></image>
|
||
|
|
</view>
|
||
|
|
<view class="item-time">{{formatTime(item.createTime)}}</view>
|
||
|
|
<view class="reply-box" v-if="item.handleOpinion">
|
||
|
|
<view class="reply-title">平台回复</view>
|
||
|
|
<view class="reply-content">{{item.handleOpinion}}</view>
|
||
|
|
<view class="item-time" v-if="item.handleTime">{{formatTime(item.handleTime)}}</view>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
<view class="empty" v-if="!loading && feedbackList.length == 0">还没有提交过投诉吐槽</view>
|
||
|
|
<view class="load-more" v-if="feedbackList.length > 0">{{hasMore ? '上拉加载更多' : '没有更多了'}}</view>
|
||
|
|
</scroll-view>
|
||
|
|
</view>
|
||
|
|
<common-loading />
|
||
|
|
</view>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
export default {
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
menuButtonInfo: {},
|
||
|
|
typeOptions: ['投诉', '吐槽', '建议', '其他'],
|
||
|
|
pictureList: [],
|
||
|
|
loading: false,
|
||
|
|
totalPages: 0,
|
||
|
|
feedbackList: [],
|
||
|
|
form: {
|
||
|
|
feedbackType: '',
|
||
|
|
problemContent: '',
|
||
|
|
contactPhone: '',
|
||
|
|
problemPicture: ''
|
||
|
|
},
|
||
|
|
searchForm: {
|
||
|
|
pageNum: 1,
|
||
|
|
pageSize: 10,
|
||
|
|
userId: uni.getStorageSync('id')
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
onLoad() {
|
||
|
|
this.menuButtonInfo = uni.getMenuButtonBoundingClientRect ? uni.getMenuButtonBoundingClientRect() : {}
|
||
|
|
this.form.contactPhone = uni.getStorageSync('mobile') || uni.getStorageSync('phone') || ''
|
||
|
|
this.getFeedbackList()
|
||
|
|
},
|
||
|
|
computed: {
|
||
|
|
hasMore() {
|
||
|
|
return this.searchForm.pageNum < this.totalPages
|
||
|
|
}
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
changeType(e) {
|
||
|
|
this.form.feedbackType = this.typeOptions[e.detail.value]
|
||
|
|
},
|
||
|
|
getArea() {
|
||
|
|
try {
|
||
|
|
return uni.getStorageSync('area') ? JSON.parse(uni.getStorageSync('area')) : {}
|
||
|
|
} catch (e) {
|
||
|
|
return {}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
getFeedbackList() {
|
||
|
|
if (this.loading) return
|
||
|
|
this.loading = true
|
||
|
|
this.tui.request("/app/problemFeedback/getProblemFeedbackList", "POST", this.searchForm, false, false).then((res) => {
|
||
|
|
this.loading = false
|
||
|
|
if (res.code == 200) {
|
||
|
|
const records = (res.result && res.result.records) || []
|
||
|
|
this.totalPages = (res.result && res.result.pages) || 0
|
||
|
|
if (this.searchForm.pageNum == 1) {
|
||
|
|
this.feedbackList = records
|
||
|
|
} else {
|
||
|
|
this.feedbackList = this.feedbackList.concat(records)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
this.tui.toast(res.message)
|
||
|
|
}
|
||
|
|
uni.hideLoading()
|
||
|
|
}).catch(() => {
|
||
|
|
this.loading = false
|
||
|
|
uni.hideLoading()
|
||
|
|
})
|
||
|
|
},
|
||
|
|
loadMore() {
|
||
|
|
if (!this.hasMore || this.loading) return
|
||
|
|
this.searchForm.pageNum += 1
|
||
|
|
this.getFeedbackList()
|
||
|
|
},
|
||
|
|
submitFeedback() {
|
||
|
|
const content = (this.form.problemContent || '').trim()
|
||
|
|
if (!content) {
|
||
|
|
this.tui.toast('请输入投诉吐槽内容')
|
||
|
|
return
|
||
|
|
}
|
||
|
|
const area = this.getArea()
|
||
|
|
const data = {
|
||
|
|
problemContent: content,
|
||
|
|
problemPicture: this.pictureList.join(','),
|
||
|
|
userId: uni.getStorageSync('id'),
|
||
|
|
userName: uni.getStorageSync('nickName') || uni.getStorageSync('nickname') || uni.getStorageSync('userName'),
|
||
|
|
contactPhone: this.form.contactPhone,
|
||
|
|
feedbackType: this.form.feedbackType || '投诉',
|
||
|
|
regionId: area.id || '',
|
||
|
|
regionName: area.title || uni.getStorageSync('schoolName') || '',
|
||
|
|
status: 0
|
||
|
|
}
|
||
|
|
this.tui.request("/app/problemFeedback/saveOrUpdateProblemFeedback", "POST", data, false, false).then((res) => {
|
||
|
|
if (res.code == 200) {
|
||
|
|
this.tui.toast('提交成功')
|
||
|
|
this.resetForm()
|
||
|
|
this.searchForm.pageNum = 1
|
||
|
|
this.getFeedbackList()
|
||
|
|
} else {
|
||
|
|
this.tui.toast(res.message)
|
||
|
|
}
|
||
|
|
uni.hideLoading()
|
||
|
|
}).catch(() => {
|
||
|
|
uni.hideLoading()
|
||
|
|
})
|
||
|
|
},
|
||
|
|
resetForm() {
|
||
|
|
this.form.feedbackType = ''
|
||
|
|
this.form.problemContent = ''
|
||
|
|
this.form.problemPicture = ''
|
||
|
|
this.pictureList = []
|
||
|
|
},
|
||
|
|
chooseImage() {
|
||
|
|
if (this.pictureList.length >= 3) {
|
||
|
|
this.tui.toast('最多上传3张图片')
|
||
|
|
return
|
||
|
|
}
|
||
|
|
uni.chooseMedia({
|
||
|
|
count: 3 - this.pictureList.length,
|
||
|
|
mediaType: ['image'],
|
||
|
|
sourceType: ['camera', 'album'],
|
||
|
|
success: (res) => {
|
||
|
|
for (let i = 0; i < res.tempFiles.length; i++) {
|
||
|
|
this.uploadFile(res.tempFiles[i].tempFilePath)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
},
|
||
|
|
async uploadFile(path) {
|
||
|
|
let hiverToken = uni.getStorageSync("hiver_token")
|
||
|
|
await uni.uploadFile({
|
||
|
|
url: this.tui.interfaceUrl() + '/upload/file',
|
||
|
|
filePath: path,
|
||
|
|
name: 'file',
|
||
|
|
header: {
|
||
|
|
"content-type": "multipart/form-data",
|
||
|
|
'accessToken': hiverToken
|
||
|
|
},
|
||
|
|
success: (uploadFileRes) => {
|
||
|
|
let pathData = JSON.parse(uploadFileRes.data)
|
||
|
|
if (pathData.result) {
|
||
|
|
this.pictureList.push(pathData.result)
|
||
|
|
}
|
||
|
|
},
|
||
|
|
fail: (err) => {
|
||
|
|
uni.showToast({
|
||
|
|
title: JSON.stringify(err),
|
||
|
|
icon: 'none'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
})
|
||
|
|
},
|
||
|
|
removeImage(index) {
|
||
|
|
this.pictureList.splice(index, 1)
|
||
|
|
},
|
||
|
|
getPictureList(picture) {
|
||
|
|
if (!picture) return []
|
||
|
|
if (Array.isArray(picture)) return picture.filter(Boolean)
|
||
|
|
return String(picture).split(',').map(item => item.trim()).filter(Boolean)
|
||
|
|
},
|
||
|
|
previewImage(index) {
|
||
|
|
uni.previewImage({
|
||
|
|
urls: this.pictureList,
|
||
|
|
current: this.pictureList[index]
|
||
|
|
})
|
||
|
|
},
|
||
|
|
previewItemImage(picture, index) {
|
||
|
|
const urls = this.getPictureList(picture)
|
||
|
|
uni.previewImage({
|
||
|
|
urls,
|
||
|
|
current: urls[index]
|
||
|
|
})
|
||
|
|
},
|
||
|
|
formatTime(value) {
|
||
|
|
if (!value) return ''
|
||
|
|
const date = new Date(value)
|
||
|
|
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}`
|
||
|
|
},
|
||
|
|
back() {
|
||
|
|
uni.navigateBack()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss">
|
||
|
|
page {
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
background: #F5F8F5;
|
||
|
|
color: #00231C;
|
||
|
|
}
|
||
|
|
|
||
|
|
.page {
|
||
|
|
min-height: 100vh;
|
||
|
|
}
|
||
|
|
|
||
|
|
.title {
|
||
|
|
height: 200rpx;
|
||
|
|
background: #F5F8F5;
|
||
|
|
}
|
||
|
|
|
||
|
|
.title-sreach {
|
||
|
|
position: relative;
|
||
|
|
display: flex;
|
||
|
|
height: 200rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.back-btn {
|
||
|
|
position: absolute;
|
||
|
|
left: 24rpx;
|
||
|
|
bottom: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.title-name {
|
||
|
|
flex: 1;
|
||
|
|
text-align: center;
|
||
|
|
font-size: 36rpx;
|
||
|
|
font-weight: 900;
|
||
|
|
}
|
||
|
|
|
||
|
|
.content {
|
||
|
|
padding: 20rpx 24rpx 40rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.add-card,
|
||
|
|
.feedback-item {
|
||
|
|
border-radius: 28rpx;
|
||
|
|
background: #fff;
|
||
|
|
box-shadow: 0 12rpx 40rpx rgba(30, 80, 60, .06);
|
||
|
|
}
|
||
|
|
|
||
|
|
.add-card {
|
||
|
|
padding: 28rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.card-title,
|
||
|
|
.list-head {
|
||
|
|
font-size: 32rpx;
|
||
|
|
font-weight: 900;
|
||
|
|
}
|
||
|
|
|
||
|
|
.type-picker,
|
||
|
|
.phone-input {
|
||
|
|
height: 82rpx;
|
||
|
|
margin-top: 22rpx;
|
||
|
|
padding: 0 24rpx;
|
||
|
|
border-radius: 20rpx;
|
||
|
|
background: #F7F8F8;
|
||
|
|
font-size: 28rpx;
|
||
|
|
line-height: 82rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.feedback-textarea {
|
||
|
|
width: 100%;
|
||
|
|
height: 210rpx;
|
||
|
|
margin-top: 22rpx;
|
||
|
|
padding: 24rpx;
|
||
|
|
border-radius: 20rpx;
|
||
|
|
background: #F7F8F8;
|
||
|
|
box-sizing: border-box;
|
||
|
|
font-size: 28rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.upload-row,
|
||
|
|
.item-pictures {
|
||
|
|
display: flex;
|
||
|
|
flex-wrap: wrap;
|
||
|
|
gap: 16rpx;
|
||
|
|
margin-top: 22rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.upload-btn,
|
||
|
|
.upload-preview image,
|
||
|
|
.item-pictures image {
|
||
|
|
width: 150rpx;
|
||
|
|
height: 150rpx;
|
||
|
|
border-radius: 18rpx;
|
||
|
|
background: #F7F8F8;
|
||
|
|
}
|
||
|
|
|
||
|
|
.upload-btn {
|
||
|
|
text-align: center;
|
||
|
|
line-height: 150rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.upload-preview {
|
||
|
|
position: relative;
|
||
|
|
}
|
||
|
|
|
||
|
|
.remove-img {
|
||
|
|
position: absolute;
|
||
|
|
top: -12rpx;
|
||
|
|
right: -12rpx;
|
||
|
|
width: 36rpx;
|
||
|
|
height: 36rpx;
|
||
|
|
border-radius: 50%;
|
||
|
|
background: #FF5A4F;
|
||
|
|
color: #fff;
|
||
|
|
text-align: center;
|
||
|
|
line-height: 36rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.submit-btn {
|
||
|
|
height: 86rpx;
|
||
|
|
margin-top: 28rpx;
|
||
|
|
border-radius: 999rpx;
|
||
|
|
background: #34C8A0;
|
||
|
|
color: #fff;
|
||
|
|
font-size: 30rpx;
|
||
|
|
font-weight: 900;
|
||
|
|
line-height: 86rpx;
|
||
|
|
text-align: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.list-head {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
margin: 34rpx 4rpx 18rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.list-tip,
|
||
|
|
.item-time {
|
||
|
|
color: #8A9199;
|
||
|
|
font-size: 22rpx;
|
||
|
|
font-weight: 400;
|
||
|
|
}
|
||
|
|
|
||
|
|
.feedback-scroll {
|
||
|
|
height: calc(100vh - 760rpx);
|
||
|
|
min-height: 520rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.feedback-item {
|
||
|
|
margin-bottom: 20rpx;
|
||
|
|
padding: 24rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.item-head {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: space-between;
|
||
|
|
}
|
||
|
|
|
||
|
|
.item-type {
|
||
|
|
font-size: 28rpx;
|
||
|
|
font-weight: 900;
|
||
|
|
}
|
||
|
|
|
||
|
|
.status {
|
||
|
|
padding: 6rpx 14rpx;
|
||
|
|
border-radius: 999rpx;
|
||
|
|
background: #FFF0E6;
|
||
|
|
color: #FF7A3D;
|
||
|
|
font-size: 22rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.status.done {
|
||
|
|
background: #E8FFF5;
|
||
|
|
color: #34C8A0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.item-content,
|
||
|
|
.reply-content {
|
||
|
|
margin-top: 16rpx;
|
||
|
|
font-size: 28rpx;
|
||
|
|
line-height: 44rpx;
|
||
|
|
word-break: break-all;
|
||
|
|
}
|
||
|
|
|
||
|
|
.reply-box {
|
||
|
|
margin-top: 20rpx;
|
||
|
|
padding: 18rpx;
|
||
|
|
border-radius: 18rpx;
|
||
|
|
background: #F7F8F8;
|
||
|
|
}
|
||
|
|
|
||
|
|
.reply-title {
|
||
|
|
color: #34C8A0;
|
||
|
|
font-weight: 900;
|
||
|
|
}
|
||
|
|
|
||
|
|
.empty,
|
||
|
|
.load-more {
|
||
|
|
padding: 38rpx 0;
|
||
|
|
color: #8A9199;
|
||
|
|
text-align: center;
|
||
|
|
}
|
||
|
|
</style>
|