988 lines
25 KiB
Vue
Raw 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 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>