apply.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <template>
  2. <view :style="themeColor()">
  3. <swiper :indicator-dots="false" :autoplay="false" :disable-touch="true" :current="step" class="h-screen" :duration="300" v-if="detail">
  4. <swiper-item>
  5. <scroll-view scroll-y="true" class="bg-page min-h-screen overflow-hidden">
  6. <view class="m-[var(--top-m)] sidebar-margin px-[var(--pad-sidebar-m)] py-[var(--pad-top-m)] rounded-[var(--rounded-big)] bg-white">
  7. <view class="flex">
  8. <view class="w-[120rpx] h-[120rpx] flex items-center justify-center">
  9. <u--image :radius="'var(--goods-rounded-small)'" width="120rpx" height="120rpx" :src="img(orderDetail.sku_image)" mode="aspectFill">
  10. <template #error>
  11. <image class="w-[120rpx] h-[120rpx] rounded-[var(--goods-rounded-small)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image>
  12. </template>
  13. </u--image>
  14. </view>
  15. <view class="flex-1 w-0 ml-[20rpx]">
  16. <view class="text-ellipsis text-[28rpx] leading-normal truncate">{{orderDetail.goods_name}}</view>
  17. <view v-if="orderDetail.sku_name" class="mt-[6rpx] text-[24rpx] leading-[1.3] text-[var(--text-color-light9)] truncate">{{ orderDetail.sku_name }}</view>
  18. </view>
  19. </view>
  20. </view>
  21. <view class="my-[var(--top-m)] sidebar-margin px-[var(--pad-sidebar-m)] rounded-[var(--rounded-big)] bg-white">
  22. <view class="py-[var(--pad-top-m)] flex items-center" @click="selectRefundType(1)">
  23. <view class="flex-1">
  24. <view class="text-[30rpx]">仅退款</view>
  25. <view class="text-[24rpx] mt-[20rpx] text-[var(--text-color-light9)]" v-if="orderDetail.goods_type !='virtual'">未收到货,或与商家协商一致不用退货只退款</view>
  26. <view class="text-[24rpx] mt-[20rpx] text-[var(--text-color-light9)]" v-else>与商家协商一致仅退款</view>
  27. </view>
  28. <text class="nc-iconfont nc-icon-youV6xx text-[28rpx] text-[var(--text-color-light9)]"></text>
  29. </view>
  30. <view class="py-[var(--pad-top-m)] flex items-center border-0 !border-t !border-[#f5f5f5] border-solid" v-if="orderDetail.goods_type == 'real' && (!orderDetail.delivery_status || orderDetail.delivery_status != 'wait_delivery')" @click="selectRefundType(2)">
  31. <view class="flex-1">
  32. <view class="text-[30rpx]">退货退款</view>
  33. <view class="text-[24rpx] mt-[20rpx] text-[var(--text-color-light9)]">已收到货,需退还收到的货物</view>
  34. </view>
  35. <text class="nc-iconfont nc-icon-youV6xx text-[28rpx] text-[var(--text-color-light9)]"></text>
  36. </view>
  37. </view>
  38. </scroll-view>
  39. </swiper-item>
  40. <swiper-item>
  41. <scroll-view scroll-y="true" class="bg-page min-h-screen overflow-hidden">
  42. <view class="my-[var(--top-m)] sidebar-margin px-[var(--pad-sidebar-m)] rounded-[var(--rounded-big)] bg-white">
  43. <view class="py-[var(--pad-top-m)] flex justify-between items-center">
  44. <view class="text-[28rpx]">退款原因</view>
  45. <view class="flex ml-[auto] items-center h-[30rpx]" @click="refundCausePopup = true">
  46. <text class="text-[26rpx] text-[var(--text-color-light9)] truncate max-w-[460rpx]">{{ formData.reason || '请选择' }}</text>
  47. <text class="nc-iconfont nc-icon-youV6xx pt-[4rpx] text-[24rpx] text-[var(--text-color-light9)]"></text>
  48. </view>
  49. </view>
  50. </view>
  51. <view class="my-[var(--top-m)] sidebar-margin px-[var(--pad-sidebar-m)] rounded-[var(--rounded-big)] bg-white">
  52. <view class="py-[var(--pad-top-m)]">
  53. <view class="flex items-center justify-between">
  54. <view class="text-[28rpx] font-500">退款金额</view>
  55. <view class="flex justify-end items-center text-[var(--price-text-color)] price-font">
  56. <text class="font-500 text-[36rpx] leading-none">¥</text>
  57. <text class="font-500 text-[36rpx] leading-none">{{ formData.apply_money }}</text>
  58. </view>
  59. </view>
  60. <view class="text-right text-[24rpx] text-[var(--text-color-light9)] mt-[10rpx]">
  61. <text v-if="refundMoney.is_refund_delivery === 1 && Number(refundMoney.refund_delivery_money) > 0" class="ml-[10rpx]">(包含运费¥{{ refundMoney.refund_delivery_money }})</text>
  62. </view>
  63. </view>
  64. </view>
  65. <view class="my-[var(--top-m)] sidebar-margin px-[var(--pad-sidebar-m)] rounded-[var(--rounded-big)] bg-white">
  66. <view class="pt-[var(--pad-top-m)] pb-[14rpx]">
  67. <view class="text-[28rpx] flex items-center">
  68. <text class="font-500">上传凭证</text>
  69. <text class="text-[24rpx] text-[var(--text-color-light9)] ml-[10rpx]">选填</text>
  70. </view>
  71. <view class="mt-[30rpx]">
  72. <u-upload :fileList="voucherListPreview"
  73. @afterRead="afterRead" @delete="deletePic" multiple :maxCount="9"></u-upload>
  74. </view>
  75. </view>
  76. </view>
  77. <view class="my-[var(--top-m)] sidebar-margin px-[var(--pad-sidebar-m)] rounded-[var(--rounded-big)] bg-white">
  78. <view class="py-[var(--pad-top-m)]">
  79. <view class="text-[28rpx] flex items-center">
  80. <text class="font-500">补充描述</text>
  81. <text class="text-[24rpx] text-[var(--text-color-light9)] ml-[10rpx]">选填</text>
  82. </view>
  83. <view class="mt-[30rpx] h-[200rpx]">
  84. <textarea class="leading-[1.5] h-[100%] w-[100%] text-[28rpx]" v-model="formData.remark" :maxlength="100" cols="30" rows="5" placeholder="补充描述,有助于更好的处理售后问题" placeholder-class="text-[26rpx] text-[var(--text-color-light9)]"></textarea>
  85. </view>
  86. </view>
  87. </view>
  88. <view class="w-full">
  89. <view class="py-[var(--top-m)] px-[var(--sidebar-m)] box-border">
  90. <button class="primary-btn-bg !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500" :loading="operateLoading" @click="save">提交</button>
  91. </view>
  92. </view>
  93. <!-- 退款原因 -->
  94. <u-popup :show="refundCausePopup" @close="refundCausePopup = false">
  95. <view class="popup-common" @touchmove.prevent.stop>
  96. <view class="title">退款原因</view>
  97. <scroll-view scroll-y="true" class="h-[450rpx] px-[30rpx] box-border">
  98. <u-radio-group v-model="currReasonName" placement="column" iconPlacement="right">
  99. <u-radio activeColor="var(--primary-color)" :labelSize="'30rpx'" labelColor="#333" :customStyle="{marginBottom: '34rpx'}" v-for="(item, index) in reason" :key="index" :label="item" :name="item"></u-radio>
  100. </u-radio-group>
  101. </scroll-view>
  102. <view class="btn-wrap">
  103. <button class="primary-btn-bg btn" @click="refundCausePopupFn">确定</button>
  104. </view>
  105. </view>
  106. </u-popup>
  107. </scroll-view>
  108. </swiper-item>
  109. </swiper>
  110. <!-- #ifdef MP-WEIXIN -->
  111. <!-- 小程序隐私协议 -->
  112. <wx-privacy-popup ref="wxPrivacyPopupRef"></wx-privacy-popup>
  113. <!-- #endif -->
  114. </view>
  115. </template>
  116. <script setup lang="ts">
  117. import { ref, computed } from 'vue'
  118. import { onLoad } from '@dcloudio/uni-app'
  119. import { redirect, img, moneyFormat, goback } from '@/utils/common'
  120. import { t } from '@/locale'
  121. import { getMallOrderDetail } from '@/addon/mall/api/order'
  122. import { getRefundReason, applyRefund,getRefundMoney } from '@/addon/mall/api/refund'
  123. import { uploadImage } from '@/app/api/system'
  124. import { useSubscribeMessage } from '@/hooks/useSubscribeMessage'
  125. const detail = ref(null)
  126. const orderDetail = ref({})
  127. const orderGoodsId = ref(0)
  128. const step = ref(0)
  129. let refundCausePopup = ref(false)
  130. const formData = ref({
  131. order_id: detail.value?.order_id,
  132. order_goods_id: orderGoodsId.value,
  133. refund_type: '',
  134. apply_money: '',
  135. reason: '',
  136. remark: '',
  137. voucher: []
  138. })
  139. let refundMoney = ref<any>({}) // 可退款金额
  140. const reason = ref<string[]>([])
  141. const currReasonName = ref('')
  142. getRefundReason().then(({ data }) => {
  143. reason.value = data
  144. if(reason.value && reason.value.length) currReasonName.value = reason.value[0];
  145. }).catch()
  146. onLoad((data) => {
  147. orderGoodsId.value = data.order_goods_id || 0
  148. formData.value.order_goods_id = orderGoodsId.value
  149. formData.value.order_id = data.order_id || 0
  150. if (data.order_id && data.order_goods_id) {
  151. getMallOrderDetail(data.order_id).then(({ data }) => {
  152. detail.value = data
  153. detail.value.order_goods.forEach((item,index)=>{
  154. if(orderGoodsId.value == item.order_goods_id){
  155. orderDetail.value = item;
  156. }
  157. })
  158. }).catch(() => {
  159. })
  160. // 获取可退款金额
  161. getRefundMoney({order_goods_id: data.order_goods_id}).then(res =>{
  162. refundMoney.value = res.data
  163. formData.value.apply_money = moneyFormat(refundMoney.value.refund_money)
  164. })
  165. } else {
  166. let parameter = {
  167. url: '/addon/mall/pages/order/list',
  168. title: '缺少订单id'
  169. };
  170. goback(parameter);
  171. }
  172. })
  173. const inputWidth = computed((value) => {
  174. return function (value) {
  175. if (value == '' || value == 0) {
  176. return '60rpx';
  177. } else {
  178. return String(value).length * 17 + 'rpx';
  179. }
  180. };
  181. })
  182. const selectRefundType = (type : number) => {
  183. formData.value.refund_type = type
  184. step.value = 1
  185. }
  186. const voucherListPreview = computed(() => {
  187. return formData.value.voucher.map(item => {
  188. return {url: img(item)}
  189. })
  190. })
  191. const afterRead = (event) => {
  192. event.file.forEach(item => {
  193. uploadImage({
  194. filePath: item.url,
  195. name: 'file'
  196. }).then(res => {
  197. if (formData.value.voucher.length < 9 ) {
  198. formData.value.voucher.push(res.data.url)
  199. }
  200. }).catch(() => {
  201. })
  202. })
  203. }
  204. const deletePic = (event)=> {
  205. formData.value.voucher.splice(event.index, 1)
  206. }
  207. const operateLoading = ref(false)
  208. const save = ()=> {
  209. if(!formData.value.reason){
  210. uni.showToast({
  211. title: '请选择退款原因',
  212. icon: 'none'
  213. });
  214. return false;
  215. }
  216. if((Number(formData.value.apply_money).toFixed(2)) < 0 ){
  217. uni.showToast({
  218. title: '退款金额不能为0,保留两位小数',
  219. icon: 'none'
  220. });
  221. return false;
  222. }
  223. if(Number(formData.value.apply_money) > Number(refundMoney.value.refund_money)) {
  224. uni.showToast({
  225. title: '退款金额不能大于可退款总额',
  226. icon: 'none'
  227. });
  228. return false;
  229. }
  230. if (operateLoading.value) return
  231. operateLoading.value = true
  232. applyRefund(formData.value).then((res) => {
  233. operateLoading.value = false
  234. // 订阅消息
  235. useSubscribeMessage().request('mall_refund_agree,mall_refund_refuse')
  236. setTimeout(()=> {
  237. redirect({ url: '/addon/mall/pages/order/detail', param: { order_id: formData.value.order_id } })
  238. }, 1000)
  239. }).catch(() => {
  240. operateLoading.value = false
  241. })
  242. }
  243. const refundCausePopupFn = ()=>{
  244. formData.value.reason = currReasonName.value;
  245. refundCausePopup.value = false;
  246. }
  247. const handleInput = (event:any) =>{
  248. if(Number(event.detail.value) > Number(refundMoney.value.refund_money)){
  249. uni.showToast({
  250. title: '退款金额不能大于可退款总额',
  251. icon: 'none'
  252. });
  253. }
  254. }
  255. </script>
  256. <style lang="scss" scoped>
  257. :deep(.u-upload__button) {
  258. width: 70px !important;
  259. height: 70px !important;
  260. border: 1px dashed #ddd;
  261. background-color: #fff;
  262. border-radius: 20rpx !important;
  263. }
  264. :deep(.u-upload__wrap__preview__image) {
  265. width: 70px!important;
  266. height: 70px!important;
  267. }
  268. </style>