988 lines
25 KiB
Vue
Raw Normal View History

2025-05-08 09:16:37 +08:00
<template>
<view class="whole">
<view class="one">
<view class="row row1" @click="showType = true">
<view class="tit">申请类型</view>
<text>{{typeObj[type]}}</text>
<up-icon name="arrow-right" />
</view>
<view class="row" @click="showReason = true">
<view class="tit">退款原因</view>
<text v-if="reason !== ''">{{refoundReasonObj[reason]}}</text>
<text v-else class="wu">请选择</text>
<up-icon name="arrow-right" />
</view>
</view>
<view class="two">
<view class="title">退款商品</view>
<view class="goodbox" v-for="(item, index) in goodsList" :key="index">
<view @click="selGoods(item)" style="display: flex;align-items: flex-start;margin-right: 10rpx;">
<span v-if="item.checked" class="iconfont icon-checked"></span>
<span v-else class="iconfont icon-circle"></span>
</view>
<view class="info">
<view class="sku" @click="selGoods(item)">
<view class="tit">{{item.goods_name}}</view>
<view class="guige">({{item.number}}<template v-if="item.sku_name">{{item.sku_name}}</template>)</view>
</view>
<view class="price">
<view class="tit">退款金额</view>
<view class="box">
<view style="display: flex;align-items: center;">
<view class="jin"></view>
<input type="text" v-model="item.amount" @blur="checkAmount(item)" class="inpt"/>
</view>
<text>最多可退{{item.limit_amount}}</text>
</view>
</view>
</view>
</view>
<view class="pickup" v-if="showFreight && can_refund_freight">
<view style="display: flex;align-items: center;" @click="takeFreight()">
<span v-if="selFreight" class="iconfont icon-checked"></span>
<span v-else class="iconfont icon-circle"></span>
<text>运费</text>
</view>
<view class="price">{{freight_price}}</view>
</view>
</view>
<view class="desc">
<view class="title">补充描述和凭证</view>
<textarea
class="text"
placeholder="补充描述,有助于商家更好的处理售后问题"
v-model="apply_desc"
maxlength="200"
@input="changeDesc($event)" />
<view class="upImg">
<view class="hasimg" v-for="(item, index) in upImg" :key="index">
<image :src="item" class="img" mode="aspectFill"></image>
<view class="icon" @click="delUpImg(item, index)"><up-icon name="close" color="#fff" size="12" /></view>
</view>
<view class="plus" @click="uploadImg()">
<up-icon name="camera-fill" color="#9D9D9D" size="24" />
<text>上传凭证最多6张</text>
</view>
</view>
</view>
<!-- 申请类型 -->
<up-popup :show="showType" :round="10" mode="bottom" close-on-click-overlay closeable @close="showType = false">
<view class="typebox">
<view class="title">请选择售后类型</view>
<view class="box">
<view class="row" v-for="item in typeList" :key="item.id" @click="selectType(item.id)">
<view>
<view class="tit">{{item.name}}</view>
<text>{{item.text}}</text>
</view>
<span v-if="item.id == type" class="iconfont icon-checked"></span>
<span v-else class="iconfont icon-circle"></span>
</view>
</view>
</view>
</up-popup>
<!-- 退款原因 -->
<up-popup :show="showReason" :round="10" mode="bottom" close-on-click-overlay closeable @close="showReason = false">
<view class="reasonbox">
<view class="title">请选择退款原因</view>
<view class="box">
<view class="row" v-for="(item, index) in backReasonList" :key="item.id" @click="selectReason(item.id)">
<view class="tit">{{item.name}}</view>
<span v-if="item.id == reason" class="iconfont icon-checked"></span>
<span v-else class="iconfont icon-circle"></span>
</view>
</view>
</view>
</up-popup>
<!-- 提交申请确认 -->
<up-modal :show="showCommit" :confirmColor="Color" :showCancelButton="true" @confirm="commitApply()" @cancel="showCommit = false">
<view style="width: 100%;">
<view class="refTitle">是否要申请退款<text>{{total_price}}</text></view>
<view class="allRefound">
<template v-for="(item, index) in goodsList" :key="index">
<view class="row" v-if="item.checked">
<view>{{item.goods_name}}{{item.sku_name}}</view>
<view>退款<text>{{item.amount}}</text></view>
</view>
</template>
<view class="row" v-if="selFreight">
<view>运费</view>
<view>退款<text>{{freight_price}}</text></view>
</view>
</view>
</view>
</up-modal>
<!-- 提交申请成功 -->
<up-modal :show="showSuccess" :showConfirmButton="false">
<view style="width: 100%;">
<view class="refTitle">退款申请已提交待团长处理
<view style="position: absolute;top: 0;right:0;" @click="torefoundDetail()">
<up-icon name="close" size="20" />
</view>
</view>
<!-- <view class="mesg">开启消息提醒团长处理后及时通知你</view>
<view style="padding: 50rpx 30rpx 30rpx;">
<view class="remind" @click="openRemind()">立即开启</view>
</view> -->
</view>
</up-modal>
<!-- 订阅次数弹窗 -->
<up-modal :show="showTake" :showConfirmButton="false">
<view style="width: 100%;position: relative;">
<view class="taketit" style="position: absolute;top: 0;right:0;">
<up-icon @click="torefoundDetail()" name="close" size="20" />
</view>
<view class="takebox">
<up-icon name="checkmark-circle" :color="Color" size="30" />
<view class="tit">订阅成功</view>
<view class="msg">为了避免错过消息提醒点击继续订阅获取后续消息通知</view>
<view style="padding: 40rpx 30rpx 10rpx">
<up-line-progress :percentage="((number/limit_number)*100).toFixed(0)" :height="5" :activeColor="Color" :showText="false" />
</view>
<text>通知次数{{number}}/{{limit_number}}</text>
</view>
<view style="padding: 50rpx 30rpx 30rpx;">
<view class="remind" @click="subscribe()">继续订阅</view>
</view>
</view>
</up-modal>
<view class="bottom">
<view @click="allSelectChange()" style="display: flex;align-items: center;font-size: 26rpx;">
<span v-if="allSelect" class="iconfont icon-checked"></span>
<span v-else class="iconfont icon-circle"></span>
<text style="margin-left: 10rpx;">全选</text>
</view>
<view style="display: flex;align-items: center;">
<view class="price">共退款:&nbsp;<text>{{total_price}}</text></view>
<view class="commit" @click="commitRefound()">提交申请</view>
</view>
</view>
<!-- 隐私协议弹窗 -->
<privacy-popup :show-dialog="showPrivacy" @close="showPrivacy = false" @agree="showPrivacy = false" @refuse="showPrivacy = false" />
</view>
</template>
<script>
import { ref, reactive, toRefs, watch } from 'vue'
import { get, post } from '@/api/request.js'
import API from '/api/index'
import { checkAmt } from '@/utils/index.js'
import { backReasonList, refoundReasonObj, Style } from '@/utils/list.js'
import { showToast, judgePrivacy } from '@/components/common.js'
import privacyPopup from '@/components/privacyPopup/index.vue'
export default {
components: {
privacyPopup
},
setup() {
const data = reactive({
order_id: '',
refund_id: '',
type: 1,
typeList: [
{id: 1, name: '我要退款(无需退货)', text: '没收到货,或与卖家协商同意不用退货只退款'},
{id: 2, name: '我要退货退款', text: '已收到货,需要退还已收到的货物'}
],
typeObj: {
'1': '我要退款(无需退货)',
'2': '我要退货退款'
},
reason: '', // 退款原因下标
goodsList: [],
freight_price: '0.00',
apply_desc: '',
selFreight: false,
upImg: [],
showType: false,
showReason: false,
total_price: '0.00',
allSelect: false,
showCommit: false,
showSuccess: false,
hasSelGoods: [],
onDialog: false,
showTake: false,
number: 0,
limit_number: 1,
backRefundId: '',
total_length: 0,
vanflag: true,
showFreight: false,
remindTmplId: '',
Color: uni.getStorageSync('theme_color'),
priceColor: Style[uni.getStorageSync('theme_index') * 1].priceColor,
showPrivacy: false,
can_refund_freight: false
})
// 获取订单信息
async function getOrderInfo() {
await get(`/api/v1/order/${data.order_id}`).then((res) => {
data.total_length = res.data.items.length
res.data.items.forEach((item) => {
// ship_status 发货状态(1, 2, 9)
if(item.could_refund_amount * 100 > 0 && item.ship_status > 0) {
item.order_item_id = item.id
item.checked = false
item.amount = item.could_refund_amount
item.limit_amount = item.could_refund_amount
data.goodsList.push(item)
}
})
data.freight_price = res.data.freight_price
if(data.goodsList.length !== data.total_length){
data.vanflag = false
} else {
data.vanflag = true
}
data.can_refund_freight = res.data.freight_price * 100 > 0 && res.data.refund_freight_price * 100 == 0
})
}
// 获取退款信息
function getRefundInfo() {
get(`/api/v1/orderRefundApply/${data.refund_id}`).then(async (res) => {
if(res.data.refunds.length !== data.total_length){
data.vanflag = false
} else {
data.vanflag = true
}
await res.data.refunds.forEach((item) => {
data.goodsList.forEach((it) => {
if(item.order_item_id == it.id) {
it.order_item_id = item.order_item_id
it.amount = item.amount
it.checked = true
data.hasSelGoods.push(it.id)
}
})
})
data.type = res.data.type
data.reason = res.data.refund_reason
data.apply_desc = res.data.apply_desc
data.selFreight = res.data.refund_type == 2 ? true : false
data.total_amount = res.data.total_amount
data.upImg = res.data.apply_pic_urls || []
})
}
// 上传图片
async function uploadImg() {
if(data.upImg.length === 6) {
showToast('已经添加了6张图片啦')
return false
}
if(await judgePrivacy()) {
data.showPrivacy = true
return false
}
data.onDialog = true
uni.chooseImage({
success(res) {
const tempFilePaths = res.tempFilePaths
uni.showLoading({
title: '正在上传...',
mask: true
})
for (let i = 0; i < tempFilePaths.length; i++) {
uni.uploadFile({
url: API.url + '/api/v1/upload',
filePath: tempFilePaths[i],
name: 'file',
header: {
Authorization: uni.getStorageSync('token') || '',
accept: 'application/json',
appid: API.appId
},
success(req) {
if(data.upImg.length < 6) {
const imgUrl = JSON.parse(req.data).data.link
data.upImg.push(imgUrl)
}
}
})
}
uni.hideLoading()
data.onDialog = false
},
fail() {
data.onDialog = false
}
})
}
function selectType(id) {
data.type = id
data.showType = false
}
function selectReason(id) {
data.reason = id
data.showReason = false
}
// 删除小图图片
function delUpImg(item, index) {
data.upImg.splice(index, 1)
}
// 选择商品
function selGoods(item) {
item.checked = !item.checked
if(item.checked) {
data.hasSelGoods.push(item.id)
// 如果该商品是多个商品同时购买的,则也须一起退货退款
if(item.goods.show_sku_style) {
data.goodsList.forEach((it) => {
if(item.goods_id == it.goods_id && item.id !== it.id) {
it.checked = true
data.hasSelGoods.push(it.id)
}
})
}
} else {
data.hasSelGoods.splice(data.hasSelGoods.indexOf(item.id), 1)
if(item.goods.show_sku_style) {
data.goodsList.forEach((it) => {
if(item.goods_id == it.goods_id && item.id !== it.id) {
it.checked = false
data.hasSelGoods.splice(data.hasSelGoods.indexOf(it.id), 1)
}
})
}
}
}
// 全选
function allSelectChange() {
data.allSelect = !data.allSelect
data.hasSelGoods = []
data.goodsList.forEach((item) => {
item.checked = data.allSelect
if(data.allSelect) {
data.hasSelGoods.push(item.id)
}
})
}
// 计算退款总金额
function countTotalPrice(){
let total_price = 0
data.goodsList.forEach((item) => {
if(item.checked) {
total_price += parseFloat(item.amount || 0) * 100
}
})
if(data.selFreight && data.can_refund_freight) {
total_price += parseFloat(data.freight_price) * 100
}
data.total_price = (total_price / 100).toFixed(2)
}
// 提交申请
function commitRefound() {
if(data.reason === '') {
showToast('请选择退款原因')
return false
}
if(data.hasSelGoods.length == 0) {
showToast('请选择需要退款的商品')
return false
}
if(data.apply_desc === '') {
showToast('请填写描述信息')
return false
}
if((data.reason == 2 || data.reason == 4 || data.reason == 5 || data.reason == 6 || data.reason == 8) && data.upImg.length === 0) {
showToast('当前退款原因需要上传图片')
return false
}
data.showCommit = true
}
function commitApply() {
uni.showLoading({
title: '正在提交...'
})
let order_items = []
data.goodsList.forEach((item) => {
if(item.checked) {
order_items.push({ order_item_id: item.id, amount: item.amount })
}
})
let params = {
type: data.type,
refund_type: data.selFreight ? 2 : 1,
refund_reason: data.reason,
order_id: data.order_id,
order_items: order_items,
apply_desc: data.apply_desc,
apply_pic_urls: data.upImg
}
if(data.refund_id) {
post('/api/v1/orderRefundApply/' + data.refund_id, params, 'PUT').then((res) => {
uni.hideLoading()
data.showCommit = false
uni.showToast({
title: '提交成功',
icon: 'success',
duration: 1000,
mask: true,
success() {
setTimeout(() => {
uni.navigateBack({
delta: 1
})
}, 1000)
}
})
})
} else {
post('/api/v1/orderRefundApply', params).then((res) => {
uni.hideLoading()
data.showCommit = false
data.showSuccess = true
data.backRefundId = res.data.id
data.number = Number(res.data.subscribe_number)
data.limit_number = Number(res.limit_number)
})
}
}
function torefoundDetail() {
data.showSuccess = false
data.showTake = false
uni.navigateBack({
delta: 1
})
}
function getRemindTempId() {
get(`/api/v1/shop/template`, { type: 21 }).then((req) => {
data.remindTmplId = req.data.template_id
})
}
// 开启提醒
function openRemind() {
data.remindTmplId && uni.requestSubscribeMessage({
tmplIds: [data.remindTmplId],
success(res) {
if (res[data.remindTmplId] === 'accept') {
post(`/api/v1/orderRefundApply/subscribe/${data.backRefundId}`).then((res) => {
data.number += 1
data.showTake = true
})
} else if (res[data.remindTmplId] == 'reject') {
uni.showModal({
title: '订阅消息',
content: '您当前拒绝接受消息通知,是否去开启',
confirmText: '开启授权',
confirmColor: '#345391',
cancelText: '仍然拒绝',
cancelColor: '#999999',
success(res) {
if (res.confirm) {
uni.openSetting({
success(res) {
},
fail(err) {
}
})
} else if (res.cancel) {
}
}
})
}
},
fail(res) {
}
})
}
// 继续订阅
function subscribe() {
if(data.number < data.limit_number) {
openRemind()
}
}
// 检查输入的退款金额
function checkAmount(item) {
if(checkAmt(item.amount)) {
let amount = parseFloat(item.amount) * 1000
let limit_amount = parseFloat(item.limit_amount) * 1000
if(amount > limit_amount) {
showToast('退款金额不可超过实付金额')
item.amount = item.limit_amount
return false
}
} else {
item.amount = item.limit_amount
return false
}
}
function changeDesc(e) {
console.log(e)
// data.apply_desc = e.detail
data.apply_desc = e.detail.value
}
function takeFreight() {
data.selFreight = !data.selFreight
countTotalPrice()
}
// 监听选择的商品去判断是否勾选运费,计算总金额
watch(data.hasSelGoods, (newSelGoods, oldSelGoods) => {
if(newSelGoods.length === data.goodsList.length) {
for (let i = 0; i < data.goodsList.length; i++) {
let obj = data.goodsList[i]
if(parseFloat(obj.amount) != parseFloat(obj.could_refund_amount)) {
data.selFreight = false
data.showFreight = false
countTotalPrice()
return false
}
}
data.selFreight = data.vanflag && data.can_refund_freight
data.showFreight = data.vanflag
data.allSelect = true
} else {
data.selFreight = false
data.showFreight = false
data.allSelect = false
}
countTotalPrice()
}, {deep: true})
// 监听商品退款金额变化判断是否勾选运费,计算总金额
watch(() => data.goodsList, (newGoods, oldGoods) => {
for (let i = 0; i < newGoods.length; i++) {
let obj = newGoods[i]
if(parseFloat(obj.amount) == parseFloat(obj.could_refund_amount) && obj.checked) {
data.selFreight = data.vanflag && data.can_refund_freight
data.showFreight = data.vanflag
} else {
data.selFreight = false
data.showFreight = false
countTotalPrice()
return false
}
}
countTotalPrice()
}, {deep: true})
return {
backReasonList,
refoundReasonObj,
...toRefs(data),
uploadImg,
delUpImg,
selectType,
selGoods,
selectReason,
allSelectChange,
commitRefound,
commitApply,
openRemind,
getOrderInfo,
checkAmount,
checkAmt,
countTotalPrice,
torefoundDetail,
getRefundInfo,
subscribe,
changeDesc,
takeFreight,
getRemindTempId
}
},
onLoad(options) {
this.order_id = options.order_id
this.refund_id = options.refund_id || ''
this.type = options.type
// this.getRemindTempId()
},
async onShow() {
if(!this.onDialog) {
this.goodsList = []
await this.getOrderInfo()
if(this.refund_id) {
this.getRefundInfo()
}
}
}
}
</script>
<style lang="scss" scoped>
.whole {
padding: 24rpx;
padding-bottom: 140rpx;
box-sizing: border-box;
.one{
background: #fff;
padding: 0 30rpx;
border-radius: 10rpx;
.row{
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 0;
&.row1{
border-bottom: 1rpx solid #E5E5E5;
}
.tit{
font-size: 26rpx;
color: #000000;
font-weight: bold;
}
text{
display: flex;
width: calc(100% - 200rpx);
font-size: 26rpx;
color: #303030;
&.wu{
color: #98989F;
}
}
}
}
.two{
background: #fff;
padding: 0 30rpx;
border-radius: 10rpx;
margin-top: 20rpx;
.title{
font-size: 26rpx;
color: #000000;
font-weight: bold;
padding: 30rpx 0;
}
.goodbox{
display: flex;
justify-content: flex-start;
padding: 20rpx 0 10rpx;
border-bottom: 1rpx solid #E5E5E5;
.info{
width: 100%;
.sku{
display: flex;
align-items: center;
font-size: 26rpx;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #E5E5E5;
.tit{
color: #303030;
}
.guige{
color: #98989F;
}
}
.price{
padding-top: 20rpx;
.tit{
color: #303030;
font-size: 26rpx;
}
.box{
display: flex;
justify-content: space-between;
align-items: center;
.inpt{
font-size: 40rpx;
color: v-bind('priceColor');
height: 70rpx;
}
}
text{
white-space: nowrap;
font-size: 24rpx;
color: #98989F;
}
}
}
}
.pickup{
font-size: 26rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0;
text{
margin-left: 10rpx;
}
}
}
.desc{
background: #fff;
padding: 0 30rpx;
border-radius: 10rpx;
margin-top: 20rpx;
.title{
font-size: 26rpx;
color: #000000;
font-weight: bold;
padding: 30rpx 0;
}
.text{
height: 80px !important;
font-size: 24rpx;
color: #333;
width: 100%;
padding: 10rpx;
box-sizing: border-box;
overflow: auto;
}
// 团购图片
.upImg{
display: flex;
flex-wrap: wrap;
padding: 20rpx 0;
.hasimg{
width: 140rpx;
height: 140rpx;
margin-right: 20rpx;
margin-bottom: 20rpx;
position: relative;
&:nth-child(4n+4){
margin-right: 0;
}
.img{
width: 100%;
height: 100%;
}
.icon{
position: absolute;
top: -18rpx;
right: -18rpx;
width: 36rpx;
height: 36rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.6);
border-radius: 50%;
z-index: 10;
}
}
.plus{
width: 140rpx;
height: 140rpx;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
border: 1px dashed #707070;
box-sizing: border-box;
border-radius: 6rpx;
text{
font-size: 24rpx;
color: #9D9D9D;
text-align: center;
}
}
}
}
.typebox{
padding: 0 30rpx;
min-height: 50vh;
.title{
font-size: 30rpx;
color: #303030;
font-weight: bold;
padding: 30rpx 0;
border-bottom: 1rpx solid #E5E5E5;
}
.box{
.row{
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1rpx solid #E5E5E5;
padding: 20rpx 0;
.tit{
font-size: 30rpx;
color: #303030;
font-weight: bold;
margin-bottom: 6rpx;
}
text{
font-size: 24rpx;
color: #999;
}
}
}
}
.reasonbox{
padding: 0 30rpx;
min-height: 50vh;
.title{
font-size: 30rpx;
color: #303030;
font-weight: bold;
padding: 30rpx 0;
border-bottom: 1rpx solid #E5E5E5;
}
.box{
padding-bottom: 100rpx;
.row{
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0;
.tit{
font-size: 28rpx;
color: #707070;
}
}
}
}
}
.refTitle{
font-size: 30rpx;
color: #000;
padding: 0 0 30rpx;
font-weight: bold;
border-bottom: 1rpx solid #eee;
position: relative;
text{
color: v-bind('priceColor');
}
}
.mesg{
font-size: 26rpx;
color: #303030;
padding: 30rpx;
text-align: center;
}
.allRefound{
padding: 20rpx 0 30rpx;
.row{
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0;
font-size: 26rpx;
color: #303030;
text{
color: v-bind('priceColor');
}
}
}
.bottom{
width: 100%;
position: fixed;
bottom: 0;
left: 0;
height: 120rpx;
padding: 0 30rpx;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
background: #fff;
z-index: 10;
.price{
font-size: 26rpx;
color: #303030;
text{
font-size: 30rpx;
color: v-bind('priceColor');
font-weight: bold;
}
}
.commit{
width: 220rpx;
height: 80rpx;
border-radius: 80rpx;
background: v-bind('Color');
color: #fff;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
margin-left: 30rpx;
}
}
.remind{
width: 100%;
height: 80rpx;
background: v-bind('Color');
font-size: 30rpx;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
border-radius: 8rpx;
}
.taketit{
position: relative;
}
.takebox{
padding: 50rpx 30rpx 20rpx;
text-align: center;
.tit{
font-size: 32rpx;
color: #333;
font-weight: bold;
padding: 20rpx 0;
}
.msg{
font-size: 26rpx;
color: #303030;
text-align: center;
padding: 0 50rpx;
line-height: 40rpx;
}
text{
font-size: 24rpx;
color: #aaa;
}
}
.mybtn{
display: flex;
align-items: center;
border-top: 1px solid #eee;
.vanbtn{
width: 50%;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
color: #666;
&.comfirm{
color: v-bind('Color');
}
}
}
.iconfont{
font-size: 18px;
}
.icon-checked{
color: v-bind('Color');
}
</style>