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

431 lines
18 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>
<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>