Преглед на файлове

feat(api): 添加售后退款相关API接口定义

- 新增 applyRefund 退款申请接口
- 新增 calculateMoney 退款金额计算接口
- 完善API类型定义,添加详细的参数说明

refactor(afterSales): 实现售后退款页面功能

- 实现商品选择和全选功能
- 添加退款金额计算逻辑
- 实现退款申请提交功能
- 优化页面交互和数据处理

fix(config): 调整开发环境配置

- 启用本地开发环境配置
- 注释线上测试环境配置

chore(api): 更新API类型定义

- 为 xsbShopList 添加 isPermiss 权限字段
- 为 myShoppingCart 添加 num 数量字段
- 修复类型定义格式问题

refactor(handlers): 临时注释登录过期提示

- 注释掉未授权错误的弹窗提示
- 保持错误抛出逻辑不变
zhangtao преди 6 дни
родител
ревизия
d0e4c08615
променени са 6 файла, в които са добавени 219 реда и са изтрити 44 реда
  1. 8 0
      src/api/api.type.d.ts
  2. 2 0
      src/api/apiDefinitions.ts
  3. 14 15
      src/api/core/handlers.ts
  4. 82 3
      src/api/globals.d.ts
  5. 2 2
      src/config/index.ts
  6. 111 24
      src/subPack-common/afterSales/index.vue

+ 8 - 0
src/api/api.type.d.ts

@@ -385,6 +385,10 @@ namespace Api {
     [property: string]: any
   }
   interface xsbShopList {
+    /**
+     * 是否有权限 1-有权限 0-无权限
+     */
+    isPermiss?: number
     /**
      * 店铺所在区域(描述)
      * 店铺所在区域
@@ -752,6 +756,10 @@ namespace Api {
      * 使用积分
      */
     useScore?: number
+    /**
+     * 前端使用字段
+     */
+    num: number
     [property: string]: any
   }
   interface myShoppingCart {

+ 2 - 0
src/api/apiDefinitions.ts

@@ -52,6 +52,8 @@ export default {
   'xsb.myShoppingCartCategory':['GET', '/smqjh-oms/app-api/v1/shoppingCart/myShoppingCartCategory'],
   'xsb.delivery':['GET', '/smqjh-system/app-api/v1/delivery'],
   'xsb.confirmReceipt':['GET', '/smqjh-oms/api/v1/order/confirmReceipt'],
+  'xsb.applyRefund':['POST', '/smqjh-oms/app-api/v1/refund/applyRefund'],
+  'xsb.calculateMoney':['POST', '/smqjh-oms/app-api/v1/refund/calculateMoney'],
 
   'common.myShoppingCart':['GET', '/smqjh-oms/app-api/v1/shoppingCart/myShoppingCart'],
   'common.addShoppingCart':['POST', '/smqjh-oms/app-api/v1/shoppingCart/addShoppingCart'],

+ 14 - 15
src/api/core/handlers.ts

@@ -1,5 +1,4 @@
 import type { Method } from 'alova'
-import router from '@/router'
 
 // Custom error class for API errors
 export class ApiError extends Error {
@@ -38,13 +37,13 @@ export async function handleAlovaResponse(
     // 如果是未授权错误,清除用户信息并跳转到登录页
     token.value = ''
     redirectName.value = getCurrentPath()
-    useGlobalMessage().confirm({
-      title: '提示',
-      msg: '登录已过期,请重新登录!',
-      success: () => {
-        router.replace({ name: 'smqjh-login' })
-      },
-    })
+    // useGlobalMessage().confirm({
+    //   title: '提示',
+    //   msg: '登录已过期,请重新登录!',
+    //   success: () => {
+    //     router.replace({ name: 'smqjh-login' })
+    //   },
+    // })
 
     throw new ApiError('登录已过期,请重新登录!', statusCode, data)
   }
@@ -86,13 +85,13 @@ export function handleAlovaError(error: any, method: Method) {
     // 如果是未授权错误,清除用户信息并跳转到登录页
     token.value = ''
     redirectName.value = getCurrentPath()
-    useGlobalMessage().confirm({
-      title: '提示',
-      msg: '登录已过期,请重新登录!',
-      success: () => {
-        router.replace({ name: 'smqjh-login' })
-      },
-    })
+    // useGlobalMessage().confirm({
+    //   title: '提示',
+    //   msg: '登录已过期,请重新登录!',
+    //   success: () => {
+    //     router.replace({ name: 'smqjh-login' })
+    //   },
+    // })
     throw new ApiError('登录已过期,请重新登录!', error.code, error.data)
   }
 

+ 82 - 3
src/api/globals.d.ts

@@ -192,8 +192,87 @@ declare global {
       ): Alova2Method<listData<Api.sysDict>, 'sys.dictPage', Config>;
     }
     xsb: {
+      calculateMoney<
+        Config extends Alova2MethodConfig<apiResData<any>> & {
+          data: {
+            orderRefundSkuList: { orderItemId: number, productCount: number }[];
+            orderNumber: string
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<any>, 'xsb.applyRefund', Config>;
+      applyRefund<
+        Config extends Alova2MethodConfig<apiResData<any>> & {
+          data: {
+            /**
+    * 退款类型 1,仅退款,2退款退货,5差价退款
+    */
+            applyType?: number;
+            /**
+             * 退款订单业务类型 1-星闪豹
+             */
+            businessType?: number;
+            /**
+             * 备注说明
+             */
+            buyerDesc?: string;
+            /**
+             * 手机号码(默认当前订单手机号码)
+             */
+            buyerMobile?: string;
+            /**
+             * 仅退款-未收到货申请原因
+             * 11(质量问题)
+             * 12(拍错/多拍/不喜欢)
+             * 3(商品描述不符)
+             * 14(假货), 15(商家发错货)
+             * 16(商品破损/少件)
+             * 17(其他)
+             * 仅退款-已收到货申请原因
+             * 51(多买/买错/不想要)
+             * 52(快递无记录)
+             * 53(少货/空包裹)
+             * 54(未按约定时间发货)
+             * 55(快递一直未送达)
+             * 56(其他)
+             * 退货退款-申请原因
+             * 101(商品破损/少件)
+             * 102(商家发错货)
+             * 103(商品描述不符)
+             * 104(拍错/多拍/不喜欢)
+             * 105(质量问题)
+             * 107(其他)
+             */
+            buyerReason?: string;
+            /**
+             * 订单编号
+             */
+            orderNumber?: string;
+            /**
+             * 退款商品详情
+             */
+            orderRefundSkuList?: AppOrderRefundSkuVo[];
+            /**
+             * 凭证图片列表使用,分割
+             */
+            photoFiles?: string;
+            /**
+             * 退款金额
+             */
+            refundAmount?: number;
+            /**
+             * 退款单类型(1:整单退款,2:单个物品退款)
+             */
+            refundType?: number;
+            [property: string]: any;
+          };
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<any>, 'xsb.applyRefund', Config>;
       nearestShop<
-        Config extends Alova2MethodConfig<apiResData<{nearestShopId:number}>> & {
+        Config extends Alova2MethodConfig<apiResData<{ nearestShopId: number }>> & {
           data: {
             latitude: number;
             longitude: number
@@ -201,7 +280,7 @@ declare global {
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<{nearestShopId:number}>, 'xsb.nearestShop', Config>;
+      ): Alova2Method<apiResData<{ nearestShopId: number }>, 'xsb.nearestShop', Config>;
       categories<
         Config extends Alova2MethodConfig<apiResData<Api.xsbCategories[]>> & {
           data: {
@@ -375,7 +454,7 @@ declare global {
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<{deliveryType:number}>, 'xsb.delivery', Config>;
+      ): Alova2Method<apiResData<{ deliveryType: number }>, 'xsb.delivery', Config>;
       confirmReceipt<
         Config extends Alova2MethodConfig<any> & {
           data: {

+ 2 - 2
src/config/index.ts

@@ -2,13 +2,13 @@ const mapEnvVersion = {
   /**
    * 开发版
    */
-  // develop: 'http://192.168.1.166:8080', // 张
+  develop: 'http://192.168.1.166:8080', // 张
   // develop: 'http://192.168.1.101:8080',
   // develop: 'http://192.168.0.157:8080',
   // develop: 'http://192.168.1.253:8080',
   // develop: 'http://192.168.1.89:8080', // 田
   // develop: 'http://47.109.84.152:8081',
-  develop: 'https://smqjh.api.zswlgz.com',
+  // develop: 'https://smqjh.api.zswlgz.com',
   /**
    * 体验版
    */

+ 111 - 24
src/subPack-common/afterSales/index.vue

@@ -1,7 +1,4 @@
 <script setup lang="ts">
-import { StaticUrl } from '@/config'
-import router from '@/router'
-
 definePage({
   name: 'common-afterSales',
   islogin: true,
@@ -11,8 +8,8 @@ definePage({
 })
 const afterSalesType = ref(1)
 const isSeletAllGoods = ref(false)
-const selectGoods = ref([])
-const fileList = ref([])
+const selectGoods = ref<number[]>([])
+const fileList = ref<{ url: string }[]>([])
 const showReson = ref(false)
 const resonId = ref()
 const resonName = ref('请选择退款原因')
@@ -23,22 +20,71 @@ const resonList = [
   { id: 4, name: '商品斤两不足' },
   { id: 5, name: '商品临期到期' },
   { id: 6, name: '商品破损/包装破损' },
+  { id: 7, name: '其他' },
 ]
 const orderList = ref<Api.xsbOmsOrderItem[]>([])
+const remark = ref('')
 onLoad((options: any) => {
   orderList.value = JSON.parse(options.order)
-  if (orderList.value.length) {
-    orderList.value.map((it) => {
-      it.num = undefined
-      return it
-    })
-  }
+  orderList.value = orderList.value.map((item: Api.xsbOmsOrderItem) => {
+    item.num = 1
+    return item
+  })
 })
+const refunMoney = ref(0)
+const isSubmit = ref(false)
 
-function handleSubmit() {
+async function handleSubmit() {
   console.log(fileList.value, 'fileList')
-  router.push({ name: 'xsb-afterSalesDetail' })
+  if (!selectGoods.value.length) {
+    useGlobalToast().show({ msg: '请选择需要退款的商品' })
+    return
+  }
+  if (!resonId.value) {
+    useGlobalToast().show({ msg: '请选择退款原因' })
+    return
+  }
+  if (!orderList.value[0].orderNumber) {
+    useGlobalToast().show({ msg: '订单数据异常' })
+    return
+  }
+  isSubmit.value = true
+  try {
+    await cathcApplRefund()
+  }
+  catch (e) {
+    isSubmit.value = false
+    console.log(e)
+  }
+  // router.push({ name: 'common-afterSalesDetail' })
+}
+
+function cathcApplRefund() {
+  uni.showLoading({ mask: true })
+  return new Promise((resolve, reject) => {
+    const filerData = orderList.value.filter(it => selectGoods.value.includes(Number(it.skuId)))
+    Apis.xsb.applyRefund({
+      data: {
+        orderNumber: orderList.value[0].orderNumber,
+        businessType: 1,
+        applyType: afterSalesType.value,
+        refundType: 2,
+        buyerReason: resonName.value,
+        buyerDesc: remark.value,
+        photoFiles: fileList.value.map(it => it.url).join(','),
+        orderRefundSkuList: filerData.map((it) => {
+          return {
+            orderItemId: it.orderItemId,
+            productCount: it.num,
+          }
+        }),
+      },
+    }).then((res) => { resolve(res) }).catch((err) => {
+      reject(err)
+    }).finally(() => { uni.hideLoading() })
+  })
 }
+
 function handleSelectReson() {
   if (!resonId.value)
     return uni.showToast({ title: '请选择退款原因', icon: 'none' })
@@ -46,9 +92,50 @@ function handleSelectReson() {
   resonName.value = resonList.find(item => item.id === resonId.value)?.name || '请选择退款原因'
 }
 
-watch(() => orderList.value, () => {
-  console.log(orderList.value, '=============')
+function handleSelectGoods() {
+  console.log(orderList.value, 'orderList', selectGoods.value)
+  if (selectGoods.value.length === orderList.value.length) {
+    isSeletAllGoods.value = true
+  }
+  else {
+    isSeletAllGoods.value = false
+    refunMoney.value = 0
+  }
+}
+function handleChangeAll() {
+  console.log(isSeletAllGoods.value, 'isSeletAllGoods')
+  if (isSeletAllGoods.value) {
+    selectGoods.value = orderList.value.map(it => it.skuId) as number[]
+  }
+  else {
+    selectGoods.value = []
+    refunMoney.value = 0
+  }
+}
+watch(() => [selectGoods.value, orderList.value], () => {
+  if (selectGoods.value.length) {
+    getReufndMoney()
+  }
 })
+async function getReufndMoney() {
+  console.log(orderList.value[0].orderNumber, 'orderList.value')
+  const filerData = orderList.value.filter(it => selectGoods.value.includes(Number(it.skuId)))
+  const res = await Apis.xsb.calculateMoney({ data: {
+    orderNumber: String(orderList.value[0].orderNumber),
+    orderRefundSkuList: filerData.map((it) => {
+      return {
+        orderItemId: it.orderItemId,
+        productCount: it.num,
+      }
+    }),
+  } })
+  refunMoney.value = res.data
+}
+function handleChangeMoney() {
+  if (selectGoods.value.length) {
+    getReufndMoney()
+  }
+}
 </script>
 
 <template>
@@ -96,23 +183,23 @@ watch(() => orderList.value, () => {
             退款商品
           </view>
           <view class="text-28rpx text-#FF4D3A font-semibold">
-            退款金额:¥21
+            退款金额:¥{{ refunMoney }}
           </view>
         </view>
         <view class="mt24rpx">
-          <wd-checkbox v-model="isSeletAllGoods">
+          <wd-checkbox v-model="isSeletAllGoods" @change="handleChangeAll">
             全选
           </wd-checkbox>
         </view>
         <view class="mt24rpx h2rpx w-full bg-#F0F0F0" />
         <view class="goods mt24rpx">
           <CollapsePanel :line-height="150">
-            <wd-checkbox-group v-model="selectGoods">
+            <wd-checkbox-group v-model="selectGoods" @change="handleSelectGoods">
               <template v-for="item in orderList" :key="item.skuId">
-                <view class="mb20rpx flex items-center justify-between">
+                <view class="flex items-center justify-between">
                   <wd-checkbox :model-value="item.skuId" />
                   <view class="box-border w-full flex items-center">
-                    <image :src="`${StaticUrl}/shu.png`" class="h120rpx w120rpx flex-shrink-0" />
+                    <image :src="item.pic" class="h120rpx w120rpx flex-shrink-0" />
                     <view class="ml20rpx box-border flex-1">
                       <view class="w-full flex justify-between">
                         <view class="max-w-320rpx whitespace-pre-wrap break-words break-all text-28rpx">
@@ -130,13 +217,13 @@ watch(() => orderList.value, () => {
                           单价:¥{{ item.price }}
                         </view>
                         <view>
-                          <wd-input-number v-model="item.num" :max="item.prodCount" :min="1" />
+                          <wd-input-number v-model="item.num" :max="item.prodCount" :min="1" @change="handleChangeMoney" />
                         </view>
                       </view>
                     </view>
                   </view>
                 </view>
-                <view class="mt20rpx h2rpx w-full bg-#F0F0F0" />
+                <view class="my20rpx h2rpx w-full bg-#F0F0F0" />
               </template>
             </wd-checkbox-group>
           </CollapsePanel>
@@ -154,7 +241,7 @@ watch(() => orderList.value, () => {
           </view>
         </view>
         <view class="mt20rpx rounded-16rpx bg-#F6F6F6 p24rpx">
-          <wd-textarea placeholder="请填写退款原因" show-word-limit :maxlength="200" />
+          <wd-textarea v-model="remark" placeholder="请填写退款原因" show-word-limit :maxlength="200" />
           <view class="upload mt18rpx">
             <Zupload v-model:value="fileList" />
           </view>
@@ -197,7 +284,7 @@ watch(() => orderList.value, () => {
     <view
       class="ios fixed bottom-0 z-10 box-border w-full w-full rounded-t-32rpx bg-white px32rpx pt-16rpx"
     >
-      <wd-button block @click="handleSubmit">
+      <wd-button block :loading="isSubmit" @click="handleSubmit">
         提交
       </wd-button>
     </view>