chuangte_bike_newxcx/page_fenbao/tousu/yhtxxq.vue

431 lines
18 KiB
Vue
Raw Normal View History

2025-08-30 17:38:15 +08:00
<template>
<view>
<u-navbar title="处理详情" :border-bottom="false" :background="bgc" back-icon-color="#fff" title-color='#fff'
title-size='36' height='50'></u-navbar>
<view class="page">
<!-- 头部 商家名称 + 状态 + 投诉详情(收起/展开) -->
<view class="card header" style="border-radius: 0;">
<view class="row between center">
<text class="label">{{xqobj.areaName == null ? '--' : xqobj.areaName}}</text>
<view class="row center right-actions">
<view class="row center status">
<view class="dot"></view>
<text class="status-text" v-if="xqobj.status == 1">商户处理中</text>
<text class="status-text" v-if="xqobj.status == 2">用户处理中</text>
<text class="status-text" v-if="xqobj.status == 3">平台处理中</text>
<text class="status-text" v-if="xqobj.status == 4">已完结</text>
</view>
<view class="toggle" @tap="toggleExpand">
<text class="toggle-text">投诉详情</text>
<text class="arrow" :class="{up: expanded}"></text>
</view>
</view>
</view>
</view>
<!-- 详情(默认收起保持结构便于后续接入真实数据) -->
<view class="card info collapsible" :class="{collapsed: !expanded}">
<view class="item"><text class="item-label">申请退款金额</text><text class="item-value emphasis">¥ {{xqobj.needRefundAmount == null ? '0' : xqobj.needRefundAmount}}</text></view>
<view class="item"><text class="item-label">已退款金额</text><text class="item-value emphasis">¥ {{xqobj.refundAmount == null ? '0' : xqobj.refundAmount}}</text></view>
<view class="item"><text class="item-label">投诉类型</text><text class="item-value">{{xqobj.title == null ? '--' : xqobj.title}}</text></view>
<view class="item"><text class="item-label">投诉编号</text><text class="item-value">{{xqobj.no == null ? '' : xqobj.no}}</text></view>
<view class="item"><text class="item-label">投诉编号</text><text class="item-value">{{xqobj.orderNo == null ? '' : xqobj.orderNo}}</text></view>
<view class="item"><text class="item-label">投诉时间</text><text class="item-value">{{xqobj.createTime == null ? '--' : xqobj.createTime}}</text></view>
<view class="item"><text class="item-label" style="width: 480rpx;">投诉原因</text><text class="item-value">{{xqobj.content == null ? '暂无投诉原因' : xqobj.content}}</text></view>
<view class="item proof">
<text class="item-label">图片凭证</text>
<view class="proof-box" v-for="(item,index) in zsimg" :key="index">
<image :src="item" @click="ClickImage(zsimg,item)" mode="aspectFill" style="border-radius: 10rpx;"></image>
</view>
</view>
</view>
<!-- 投诉申请已提交卡片 -->
<view class="card submit-card" style="border-radius: 16rpx;width: 680rpx;margin: auto;margin-top: 20rpx;">
<view class="row center">
<view class="check-icon"><text class="check"></text></view>
<text class="submit-title" v-if="xqobj.status == 1">投诉申请已提交</text>
<text class="submit-title" v-if="xqobj.status == 2">商家已反馈</text>
<text class="submit-title" v-if="xqobj.status == 3">投诉申请已提交</text>
<text class="submit-title" v-if="xqobj.status == 4">投诉申请已完结</text>
</view>
<text class="submit-sub" v-if="xqobj.status == 1">商家将在一天内处理请耐心等待</text>
<text class="submit-sub" v-if="xqobj.status == 2">请在一天内处理</text>
<text class="submit-sub" v-if="xqobj.status == 3">平台将在一天内处理请耐心等待</text>
<text class="submit-sub" v-if="xqobj.status == 4">已完结</text>
</view>
<!-- 对话区基于 msgList 渲染 -->
<view class="chat" v-if="Array.isArray(msgList) && msgList.length">
<block v-for="(msg, idx) in msgList" :key="msg.id || idx">
<!-- 系统消息居中展示无头像 -->
<view v-if="msg.type == 3" class="sys">
<view class="pill">
<text>{{ msg.content || '系统通知' }}</text>
</view>
</view>
<!-- 退款消息显示退款卡片 -->
<view v-else-if="msg.type == 4" class="refund card" style="width: 680rpx;margin: auto;margin-top: 20rpx;">
<view class="refund-row">
<view class="check-icon solid"><text class="check inverse"></text></view>
<text class="refund-text">退款已到账</text>
</view>
<view class="refund-amount">
<text class="emphasis">¥ {{ msg.content }}</text>
<text class="light-text" v-if="xqobj.partRefund">已部分退款</text>
</view>
</view>
<!-- 用户/商户消息左右气泡头像在上名称在下颜色区分角色 -->
<block v-else>
<view class="row chat-row" :class="{ 'right-row': isRight(msg) }">
<!-- 左侧商户头像+名称列 在左气泡在右 -->
<view v-if="!isRight(msg)" class="person left">
<view class="avatar merchant"></view>
<text class="name merchant">{{ msg.sendName }}</text>
</view>
<view class="bubble" :class="[ isRight(msg) ? 'right user' : 'left merchant' ]">
<text v-if="msg.content">{{ msg.content }}</text>
<!-- 图片列表 -->
<view v-if="msg.picture" style="margin-top: 12rpx; display:flex; flex-wrap:wrap; gap:12rpx;">
<image v-for="(pic, pidx) in splitPics(msg.picture)" :key="pidx" :src="pic" mode="aspectFill" style="width: 180rpx; height: 180rpx; border-radius: 8rpx;" @click="ClickImage(splitPics(msg.picture), pic)"></image>
</view>
</view>
<!-- 右侧用户气泡在左头像+名称列 在右 -->
<view v-if="isRight(msg)" class="person right">
<view class="avatar user"></view>
<text class="name user">{{ msg.sendName }}</text>
</view>
</view>
<view class="time" :class="{ right: isRight(msg) }" v-if="msg.createTime">{{ msg.createTime }}</view>
</block>
</block>
</view>
<!-- 退款结果条由 msgList type==4 消息驱动不再单独静态展示 -->
<!-- 底部按钮 -->
<view class="footer">
<button class="primary" @tap="onContinue">继续投诉</button>
<view class="sqpt" v-if="xqobj.platform == true">
平台已介入
</view>
<view v-else class="sqpt" @click="btnptjr">
申请平台介入
</view>
</view>
<!-- 继续投诉弹窗自定义 -->
<view v-if="showContinue" class="modal-mask" @touchmove.stop.prevent>
<view class="modal-panel">
<view class="popup-header">
<text class="popup-title">继续投诉</text>
<text class="popup-close" @tap="showContinue=false">×</text>
</view>
<view class="popup-body">
<view class="textarea-box">
<textarea v-model="continueText" maxlength="100" :auto-height="true" placeholder="请输入继续投诉内容" placeholder-class="ph"/>
<text class="count">{{ continueText.length }}/100</text>
</view>
<view class="uploader">
<view class="upload-item add" v-if="images.length < 4" @tap="pickImages">
<image src="https://api.ccttiot.com/smartmeter/img/static/udgL6KzisrZlMrk9AZb2" mode=""></image>
</view>
<view class="upload-item" v-for="(img,idx) in images" :key="idx">
<image :src="img" mode="aspectFill"></image>
<view class="del" @tap="removeImage(idx)">×</view>
</view>
</view>
<text class="tip">最多上传4张</text>
</view>
<view class="popup-footer">
<button class="primary" @tap="submitContinue">确认投诉</button>
<view class="sqpt" v-if="xqobj.platform == true">
平台已介入
</view>
<view v-else class="sqpt" @click="btnptjr">
申请平台介入
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
bgc: {
backgroundColor: "#4C97E7",
},
expanded: false,
showContinue: false,
continueText: '',
images: [],
token:'',
id:'',
xqobj:{},
zsimg:[],
msgList:[]
}
},
onLoad(option) {
this.id = option.id
this.getxq()
this.getqiniuyun()
},
methods: {
// 工具:是否右侧(用户)
isRight(msg){
return String(msg.type) == '1'
},
// 工具:展示名称
getSenderName(msg){
const name = msg && msg.sendName
if (name) return name
if(String(msg.type) == '2') return this.xqobj.mchName || '商户'
if(String(msg.type) == '1') return '用户'
return '系统'
},
// 工具:角色标签
getRoleLabel(msg){
if(String(msg.type) == '1') return '用户'
if(String(msg.type) == '2') return '商户'
return '系统'
},
// 工具:图片字符串切分
splitPics(picStr){
if(!picStr){ return [] }
return picStr.split(',').filter(Boolean)
},
// 工具:退款金额
getRefundAmount(msg){
// 优先从消息体取,其次从详情对象兜底
return (msg.amount || msg.refundAmount || this.xqobj.refundAmount || this.xqobj.needRefundAmount || 0)
},
// 点击申请平台介入
btnptjr(){
let that = this
uni.showModal({
title: '提示',
content: '您确定要申请平台介入吗?',
success: function(res) {
if (res.confirm) {
that.$u.put(`/app/complaint/platform?id=${that.id}`).then((res) => {
if (res.code == 200) {
uni.showToast({ title:'申请成功', icon:'success',duration:3000})
that.getxq()
}else{
uni.showToast({ title:res.msg, icon:'none',duration:3000});
}
})
}
}
})
},
// 获取七牛云上传token
getqiniuyun(){
this.$u.get("/common/qiniuToken").then((res) => {
if (res.code == 200) {
this.token = res.token
}
})
},
// 点击放大查看图片
ClickImage(PhotoAddress, index) {
uni.previewImage({
urls: PhotoAddress, //需要预览的图片http链接列表
current: index, // 当前显示图片的http链接默认是第一个
success: function(res) {},
fail: function(res) {},
complete: function(res) {},
})
},
// 获取订单详情
getxq(){
this.$u.get(`/app/complaint/detail?id=${this.id}&showMsg=true&showOrder=true`).then((res) => {
if (res.code == 200) {
this.xqobj = res.data || {}
this.zsimg = res.data.picture ? res.data.picture.split(',').filter(Boolean) : []
let list = res.data.msgList
if (!Array.isArray(list) || list.length === 0) {
const init = []
// 系统提交提示
if (this.xqobj && (this.xqobj.status == 1 || this.xqobj.status == '1')) {
init.push({ id: 'sys-submit', type: 3, content: '投诉申请已提交', createTime: this.xqobj.createTime })
}
// 首条用户投诉内容
// if (this.xqobj && this.xqobj.content) {
// init.push({ id: 'init-user', type: 1, content: this.xqobj.content, picture: this.xqobj.picture, createTime: this.xqobj.createTime, sendName: this.xqobj.userName })
// }
list = init
}
this.msgList = list
}
})
},
toggleExpand(){
this.expanded = !this.expanded
},
// 继续申诉
onContinue(){
this.showContinue = true
},
// 选择图片
pickImages() {
let _this = this
let math = 'static/' + _this.$u.guid(20)
uni.chooseImage({
count: 4 - this.images.length,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success(res) {
const tempFilePaths = res.tempFilePaths[0]
wx.uploadFile({
url: 'https://up-z2.qiniup.com',
name: 'file',
filePath: tempFilePaths,
formData: {
token: _this.token, //后端返回的token
key: 'smartmeter/img/' + math
},
success: function(res) {
console.log(res, 'resres');
let str = JSON.parse(res.data)
let tempFilePaths = 'https://api.ccttiot.com/' + str.key
_this.images.push(tempFilePaths)
console.log(_this.images);
}
})
}
})
},
removeImage(index){
this.images.splice(index,1)
},
submitContinue(){
let img = this.images.join(',')
let data = {
complaintId:this.id,
content:this.continueText,
picture:img
}
this.$u.put(`/app/complaint/continue`,data).then((res) => {
if (res.code == 200) {
uni.showToast({ title:'已提交', icon:'success',duration:3000})
this.getxq()
this.showContinue = false
this.continueText = ''
this.images = []
}else{
uni.showToast({ title:res.msg, icon:'none',duration:3000})
}
})
}
}
}
</script>
<style lang="scss">
.page{ max-height: 99999rpx;
padding-bottom: 20vh;}
.card{ background:#fff; border-radius:0 0 16rpx 16rpx; padding: 20rpx 24rpx;box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.04); }
.row{ display:flex; }
.between{ justify-content: space-between; }
.center{ align-items: center; }
/* header */
.label{ font-size: 28rpx; color:#111; font-weight: 600; }
.right-actions{ gap: 16rpx; }
.status .dot{ width: 18rpx; height: 18rpx; border-radius: 50%; background:#4C97E7; margin-right: 8rpx; }
.status-text{ color:#4C97E7; font-size: 24rpx; }
.toggle{ margin-left: 20rpx; display:flex; align-items:center; }
.toggle-text{ color:#9EA3AA; font-size: 24rpx; }
.arrow{ width: 0; height: 0; border-left: 10rpx solid transparent; border-right: 10rpx solid transparent; border-top: 12rpx solid #9EA3AA; margin-left: 8rpx; transform: rotate(0deg); transition: transform .2s ease; }
.arrow.up{ transform: rotate(180deg); }
/* 详情(可折叠) */
.collapsible{ overflow: hidden; transition: all .24s ease; }
.collapsed{ height: 0; padding-top: 0; padding-bottom: 0; margin-top: 0; box-shadow: none; }
.info .item{ display:flex; justify-content: space-between; padding: 18rpx 0; border-bottom: 1rpx solid #F2F3F5; }
.info .item:last-child{ border-bottom: none; }
.item-label{ color:#666; font-size: 26rpx; }
.item-value{ color:#111; font-size: 26rpx; }
.emphasis{ color:#111; font-weight: 700; }
.proof{ align-items: center; }
.proof-box{ width: 140rpx; height: 140rpx; background:#EDEFF2; border-radius: 8rpx; }
/* 提交卡片 */
.submit-card{ padding: 20rpx 24rpx; }
.check-icon{ width: 36rpx; height: 36rpx; border-radius: 50%; background:#E6F6EC; display:flex; align-items:center; justify-content:center; }
.check-icon.solid{ background:#4C97E7; }
.check{ color:#4C97E7; font-size: 24rpx; }
.check.inverse{ color:#fff; }
.submit-title{ margin-left: 12rpx; color:#111; font-weight: 600; font-size: 28rpx; }
.submit-sub{ display:block; margin-top: 8rpx; color:#9EA3AA; font-size: 24rpx; }
/* 对话区 */
.chat{ padding: 0 20rpx; padding-top: 20rpx;}
.from{ color:#9EA3AA; font-size: 22rpx; margin: 16rpx 0 2rpx 0; }
.from.right{ text-align: right; }
.name{ color:#9EA3AA; font-size: 22rpx; margin: 16rpx 0 2rpx 0; }
.name.right{ text-align: right; }
.name.user{ color:#4C97E7; }
.name.merchant{ color:#333; }
.chat-row{ align-items:flex-end; margin-top: 8rpx; }
.right-row{ justify-content:flex-end; }
.person{ display:flex; flex-direction: column; align-items:center; gap: 6rpx; }
.person.left{ margin-right: 12rpx; }
.person.right{ margin-left: 12rpx; }
.avatar{ width: 60rpx; height: 60rpx; border-radius: 50%; margin: 0 12rpx; }
.avatar.user{ background:#D7EEE2; }
.avatar.merchant{ background:#E6E8EB; }
.bubble{ max-width: 84%; padding: 16rpx 20rpx; border-radius: 16rpx; margin: 8rpx 0; font-size: 28rpx; line-height: 1.7; }
.bubble.left.merchant{ background:#fff; border: 1rpx solid #ECEDEF; }
.bubble.right.user{ background:#E6F6EC; color:#4C97E7; }
.time{ color:#9EA3AA; font-size:22rpx; margin-top: 10rpx; }
.time.right{ text-align: right; }
/* 角色小标签 */
.role-tag{ color:#666; }
.role-tag.user{ color:#4C97E7; }
.role-tag.merchant{ color:#333; }
/* 系统消息居中 pill */
.sys{ display:flex; flex-direction: column; align-items:center; margin: 18rpx 0; }
.sys .pill{ background:#F3F4F6; color:#666; font-size: 24rpx; padding: 10rpx 16rpx; border-radius: 40rpx; max-width: 84%; text-align:center; }
.sys-time{ color:#B0B5BB; font-size: 22rpx; margin-top: 8rpx; text-align:center; }
/* 退款条 */
.refund-row{ display:flex; align-items:center; padding: 24rpx 0; border-bottom:1rpx solid #F2F3F5; }
.refund-text{ color:#111; font-weight:600; font-size:28rpx; margin-left: 12rpx; }
.refund-link{ margin-left:auto; color:#4C97E7; font-size:24rpx; margin-right: 6rpx; }
.chev{ color:#C0C4CC; font-size: 36rpx; line-height: 1; }
.refund-amount{ display:flex; align-items:center; padding-top: 18rpx; font-size:26rpx; color:#666; }
.refund-amount .emphasis{ color:#111; margin: 0 8rpx; font-weight: 700; }
.light-text{ color:#9EA3AA; }
/* 底部按钮 */
.footer{ position: fixed; left: 0; right: 0; bottom: 0; background:#fff; box-shadow: 0 -6rpx 16rpx rgba(0,0,0,0.06);padding-top: 30rpx;height: 240rpx;}
.primary{ background:#4C97E7; color:#fff; border-radius: 12rpx; height: 88rpx; line-height: 88rpx; font-size: 30rpx; width: 88%; }
/* 弹窗 */
.modal-mask{ position: fixed; left:0; right:0; top:0; bottom:0; background: rgba(0,0,0,.45); display:flex; align-items:flex-end; z-index: 9999; }
.modal-panel{ width: 100%; background:#fff; border-radius: 16rpx 16rpx 0 0; }
.popup-wrap{ background:#fff; border-radius: 16rpx 16rpx 0 0; padding-bottom: env(safe-area-inset-bottom); }
.popup-header{ position: relative; padding: 24rpx; border-bottom: 1rpx solid #F0F1F3; text-align: center; }
.popup-title{ font-size: 30rpx; color:#111; font-weight: 600; }
.popup-close{ position: absolute; right: 24rpx; top: 20rpx; font-size: 40rpx; color:#9EA3AA; }
.popup-body{ padding: 20rpx 24rpx; }
.textarea-box{ position: relative; background:#fff; border-bottom: 1rpx solid #F0F1F3; min-height: 180rpx; }
.textarea-box textarea{ width: 100%; min-height: 160rpx; font-size: 28rpx; padding: 12rpx 0; }
.ph{ color:#B6BCC3; }
.count{ position: absolute; right: 0; bottom: 8rpx; color:#B6BCC3; font-size: 22rpx; }
.uploader{ display: flex; flex-wrap: wrap; gap: 16rpx; margin-top: 20rpx; }
.upload-item{ width: 140rpx; height: 140rpx; border-radius: 8rpx; position: relative; overflow: hidden; background:#fff; }
.upload-item image{ width: 140rpx; height: 140rpx; }
.upload-item.add{ display:flex; align-items:center; justify-content:center; color:#B6BCC3; font-size: 48rpx; }
.upload-item .del{ position:absolute; right: 6rpx; top: 6rpx; width: 36rpx; height: 36rpx; line-height: 36rpx; text-align:center; border-radius: 50%; background: rgba(0,0,0,.45); color:#fff; font-size: 26rpx; }
.tip{ display:block; color:#9EA3AA; font-size: 22rpx; margin-top: 8rpx; }
.popup-footer{ padding: 16rpx 24rpx 46rpx; }
.sqpt{width: 100%;text-align: center;color: #4C97E7;margin-top: 30rpx;}
</style>