chuangte_bike_newxcx/page_fenbao/tousu/shtsxq.vue
2025-08-30 17:38:15 +08:00

513 lines
22 KiB
Vue
Raw Permalink 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>
<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>
</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 user"></view>
<text class="name user">{{ msg.sendName }}</text>
</view>
<view class="bubble" :class="[ isRight(msg) ? 'right merchant' : 'left user' ]">
<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 merchant"></view>
<text class="name merchant">{{ 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">
<view class="button-group">
<button class="refund-btn" @tap="onRefund">退款</button>
<button class="primary" @tap="onContinue">继续回复</button>
</view>
<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 v-if="showRefund" class="modal-mask" @touchmove.stop.prevent>
<view class="modal-panel">
<view class="popup-header">
<text class="popup-title">申请退款</text>
<text class="popup-close" @tap="showRefund=false">×</text>
</view>
<view class="popup-body">
<view class="refund-input-box">
<text class="refund-label">退款金额</text>
<view class="amount-input">
<text class="currency">¥</text>
<input v-model="refundAmount" type="digit" placeholder="请输入退款金额" placeholder-class="ph" class="refund-input"/>
</view>
<text class="refund-tip">可退款金额:¥{{ xqobj.maxRefundAmount }}</text>
</view>
</view>
<view class="popup-footer" style="display: flex;">
<button class="cancel-btn" @tap="showRefund=false">取消</button>
<button class="primary" @tap="submitRefund">确定</button>
</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:[],
showRefund: false,
refundAmount: ''
}
},
onLoad(option) {
this.id = option.id
this.getxq()
this.getqiniuyun()
},
methods: {
// 工具:是否右侧(用户)
isRight(msg){
return String(msg.type) == '2' || String(msg.type) == '5'
},
// 工具:展示名称
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)
},
// 获取最大可退款金额
getMaxRefundAmount(){
return this.xqobj.needRefundAmount || this.xqobj.refundAmount || 0
},
// 点击申请平台介入
btnptjr(){
let that = this
uni.showModal({
title: '提示',
content: '您确定要申请平台介入吗?',
success: function(res) {
if (res.confirm) {
that.$u.put(`/bst/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(`/bst/complaint/${this.id}?showMsg=true&showOrder=true`).then((res) => {
if (res.code == 200) {
this.xqobj = res.data || {}
this.zsimg = res.data && res.data.picture ? res.data.picture.split(',').filter(Boolean) : []
let list = (res.data && res.data.msgList) ? 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 })
}
list = init
}
this.msgList = list
}
})
},
toggleExpand(){
this.expanded = !this.expanded
},
// 退款
onRefund(){
this.showRefund = true
this.refundAmount = ''
},
// 继续申诉
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.post(`/bst/complaint/reply`,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})
}
})
},
// 提交退款申请
submitRefund(){
if (!this.refundAmount || this.refundAmount <= 0) {
uni.showToast({ title:'请输入有效的退款金额', icon:'none',duration:3000})
return
}
const amount = parseFloat(this.refundAmount)
const maxAmount = this.getMaxRefundAmount()
if (amount > maxAmount) {
uni.showToast({ title:'退款金额不能超过可退款金额', icon:'none',duration:3000})
return
}
let that = this
uni.showModal({
title: '确认退款',
content: `确定要申请退款 ¥${amount} 吗?`,
success: function(res) {
if (res.confirm) {
that.$u.put(`/bst/complaint/refund`, {
complaintId: that.id,
refundAmount: amount
}).then((res) => {
if (res.code == 200) {
uni.showToast({ title:'退款申请成功', icon:'success',duration:3000})
that.getxq()
that.showRefund = false
}else{
uni.showToast({ title:res.msg, icon:'none',duration:3000});
}
})
that.showRefund = false
}
}
})
}
}
}
</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; }
.bubble.right.merchant{ background:#E6F6EC; color:#4C97E7; }
.bubble.left.user{ background:#fff; border: 1rpx solid #ECEDEF; }
.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: 0 30rpx; padding-top: 30rpx;height: 240rpx;}
.button-group{ display: flex; gap: 20rpx; margin-bottom: 20rpx; }
.refund-btn{ background:#fff; color:#4C97E7; border: 2rpx solid #4C97E7; border-radius: 12rpx; height: 88rpx; line-height: 88rpx; font-size: 30rpx; flex: 1; }
.primary{ background:#4C97E7; color:#fff; border-radius: 12rpx; height: 88rpx; line-height: 88rpx; font-size: 30rpx; flex: 1; }
/* 弹窗 */
.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; }
.popup-footer .cancel-btn{ background:#fff; color:#666; border: 2rpx solid #E5E5E5; border-radius: 12rpx; height: 88rpx; line-height: 88rpx; font-size: 30rpx; flex: 1; margin-right: 20rpx; }
.popup-footer .primary{ flex: 1; }
.sqpt{width: 100%;text-align: center;color: #4C97E7;margin-top: 30rpx;}
/* 退款弹窗样式 */
.refund-input-box{ padding: 20rpx 0; }
.refund-label{ display: block; color:#111; font-size: 28rpx; font-weight: 600; margin-bottom: 20rpx; }
.amount-input{ display: flex; align-items: center; border: 2rpx solid #E5E5E5; border-radius: 12rpx; padding: 0 20rpx; height: 88rpx; background: #fff; }
.currency{ color:#111; font-size: 32rpx; font-weight: 600; margin-right: 16rpx; }
.refund-input{ flex: 1; height: 88rpx; font-size: 32rpx; color:#111; }
.refund-tip{ display: block; color:#9EA3AA; font-size: 24rpx; margin-top: 16rpx; }
</style>