Forráskód Böngészése

```
feat(charge): 添加充值档位和购券记录功能

- 新增 RechargeLevel 接口定义,包含充值金额、档位名称、状态等字段
- 新增 chargeLevelRequest 和 PurchaseRecordVO 接口定义
- 添加 totalMoney 字段到实际总费用属性
- 新增 getReChargeLevel、addPurchaseRecord、wxJsApiPay、
getPurchaseRecordPage 等API接口定义
- 更新全局类型声明,修复类型引用问题
- 修改开发环境配置,启用邓的本地开发地址
- 将 chargeVoucher 页面设置为需要登录访问
- 在购券记录列表页面实现分页加载和状态显示功能
- 在订单详情页面更新费用显示,添加积分扣减和平米券信息
- 在充值券页面实现选择充值档位和微信支付功能
- 更新首页用户信息显示和平台券余额展示
```

zouzexu 2 napja
szülő
commit
5a2860a387

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

@@ -2334,7 +2334,72 @@ namespace Api {
      * 充电电费
      */
     totalMoney?: number
+    /**
+     * 用户实付
+     */
+    actualTotal?: number
+    platformVolume?: number
+  }
+
+  export interface RechargeLevel {
+    createTime?: string
+    /**
+     * 逻辑删除(0-未删除 1-已删除)
+     */
+    isDeleted?: number
+    /**
+     * 充值金额
+     */
+    money?: number
+    /**
+     * 充值档位名称
+     */
+    name?: string
+    /**
+     * 状态 (0-不可用 1-可用)
+     */
+    status?: number
+    /**
+     * 充值提示
+     */
+    tips?: string
+    updateTime?: string
+  }
+
+  export interface chargeLevelRequest {
+    /**
+     * 收货人电话
+     */
+    consigneeMobile?: string
+    /**
+     * 收货人姓名
+     */
+    consigneeName?: string
+    /**
+     * 购券金额
+     */
+    couponAmount?: number
+    [property: string]: any
+  }
 
+  export interface PurchaseRecordVO {
+    /**
+     * 购券金额
+     */
+    couponAmount?: number
+    /**
+     * 购卷时间
+     */
+    createTime?: string
+    /**
+     * 购券记录编号
+     */
+    recordNo?: string
+    /**
+     * 记录状态:1-已取消 2-已到账 3-退款
+     */
+    recordStatus?: number
+    [property: string]: any
   }
 
   interface videoRightHomePage {

+ 4 - 0
src/api/apiDefinitions.ts

@@ -99,6 +99,10 @@ export default {
   'charge.stopCharge': ['POST', '/smqjh-system/applet/v1/station/stopCharge'],
   'charge.stationInfoMapList': ['GET', '/smqjh-system/applet/v1/homePage/getStationInfoMapList'],
   'charge.orderDetail': ['GET', '/smqjh-oms/api/v1/order/getChargeOrder'],
+  'charge.getReChargeLevel': ['GET', '/smqjh-system/applet/v1/purchaseRecord/getReChargeLevel'],
+  'charge.addPurchaseRecord': ['POST', '/smqjh-system/applet/v1/purchaseRecord/addPurchaseRecord'],
+  'charge.wxJsApiPay': ['POST', '/smqjh-oms/service/pay/jsapi'],
+  'charge.getPurchaseRecordPage': ['POST', '/smqjh-system/applet/v1/purchaseRecord/getPurchaseRecordPage'],
 
   'videoRight.findAppByPage': ['GET', '/smqjh-pms/app-api/v1/videoProduct/findAppByPage'],
   'videoRight.goodsDetail': ['GET', '/smqjh-pms/app-api/v1/videoProduct/findById'],

+ 60 - 15
src/api/globals.d.ts

@@ -206,7 +206,7 @@ declare global {
     }
     xsb: {
       popupConfig<
-        Config extends Alova2MethodConfig<apiResData<any>> & { }
+        Config extends Alova2MethodConfig<apiResData<any>> & {}
       >(
         config: Config
       ): Alova2Method<apiResData<any>, 'xsb.popupConfig', Config>;
@@ -914,19 +914,64 @@ declare global {
       ): Alova2Method<apiResData<any>, 'charge.stationInfoMapList', Config>;
 
       orderDetail<
-        Config extends Alova2MethodConfig<apiResData<chargeOrderDetail>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.chargeOrderDetail>> & {
           data: {
             orderNumber?: string | null;
           }
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<chargeOrderDetail>, 'charge.orderDetail', Config>;
+      ): Alova2Method<apiResData<Api.chargeOrderDetail>, 'charge.orderDetail', Config>;
+
+      getReChargeLevel<
+        Config extends Alova2MethodConfig<apiResData<Api.RechargeLevel[]>> & {}
+      >(
+        config: Config
+      ): Alova2Method<apiResData<Api.RechargeLevel[]>, 'charge.getReChargeLevel', Config>;
+
+      addPurchaseRecord<
+        Config extends Alova2MethodConfig<apiResData<string>> & {
+          data: {
+            couponAmount?: number;
+            consigneeName?: string;
+            consigneeMobile?: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<string>, 'charge.addPurchaseRecord', Config>;
+
+      wxJsApiPay<
+        Config extends Alova2MethodConfig<apiResData<wxpay>> & {
+          data: {
+            orderNumber?: string;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<apiResData<wxpay>, 'charge.wxJsApiPay', Config>;
+
+      getPurchaseRecordPage<
+        Config extends Alova2MethodConfig<listData<Api.PurchaseRecordVO>> & {
+          data: {
+            /**
+             * 页码
+             */
+            pageNum?: number;
+            /**
+             * 每页记录数
+             */
+            pageSize?: number;
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<listData<Api.PurchaseRecordVO>, 'charge.getPurchaseRecordPage', Config>;
     }
 
     videoRight: {
       findAppByPage<
-        Config extends Alova2MethodConfig<apiResData<videoRightHomePage>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.videoRightHomePage>> & {
           data: {
             pageNum?: number;
             pageSize?: number;
@@ -935,48 +980,48 @@ declare global {
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<videoRightHomePage>, 'videoRight.findAppByPage', Config>;
+      ): Alova2Method<apiResData<Api.videoRightHomePage>, 'videoRight.findAppByPage', Config>;
 
       goodsDetail<
-        Config extends Alova2MethodConfig<apiResData<VideoProductVo>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.VideoProductVo>> & {
           data: {
             id: string
           }
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<VideoProductVo>, 'videoRight.goodsDetail', Config>;
+      ): Alova2Method<apiResData<Api.VideoProductVo>, 'videoRight.goodsDetail', Config>;
 
       goodsPreview<
-        Config extends Alova2MethodConfig<apiResData<VideoProductOrderPreviewVo>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.VideoProductOrderPreviewVo>> & {
           data: {
             id: string
           }
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<VideoProductOrderPreviewVo>, 'videoRight.goodsPreview', Config>;
+      ): Alova2Method<apiResData<Api.VideoProductOrderPreviewVo>, 'videoRight.goodsPreview', Config>;
 
       orderCreate<
-        Config extends Alova2MethodConfig<videoRightCreateOrder> & {
+        Config extends Alova2MethodConfig<Api.videoRightCreateOrder> & {
           data: Api.videoRightsubmitOrder;
         }
       >(
         config: Config
-      ): Alova2Method<videoRightCreateOrder, 'videoRight.orderCreate', Config>;
+      ): Alova2Method<Api.videoRightCreateOrder, 'videoRight.orderCreate', Config>;
 
       orderDetail<
-        Config extends Alova2MethodConfig<apiResData<VirtualOrderDetailVo>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.VirtualOrderDetailVo>> & {
           data: {
             orderNumber: string
           }
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<VirtualOrderDetailVo>, 'videoRight.goodsPreview', Config>;
+      ): Alova2Method<apiResData<Api.VirtualOrderDetailVo>, 'videoRight.goodsPreview', Config>;
 
       orderList<
-        Config extends Alova2MethodConfig<apiResData<DataVirtualOrderListVo>> & {
+        Config extends Alova2MethodConfig<apiResData<Api.DataVirtualOrderListVo>> & {
           data: {
             rechargeStatus?: string
             pageNum?: number
@@ -985,7 +1030,7 @@ declare global {
         }
       >(
         config: Config
-      ): Alova2Method<apiResData<DataVirtualOrderListVo>, 'videoRight.orderList', Config>;
+      ): Alova2Method<apiResData<Api.DataVirtualOrderListVo>, 'videoRight.orderList', Config>;
     }
     djk: {
       appletGoodsList<

+ 2 - 2
src/config/index.ts

@@ -6,14 +6,14 @@ const mapEnvVersion = {
   // 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.0.19:8080', // 邓
+  develop: 'http://192.168.0.19:8080', // 邓
   // develop: 'http://192.168.0.217:8080', // 黄
   // develop: 'http://192.168.0.11:8080', // 王
   // develop: 'http://192.168.1.89:8080', // 田
   // develop: 'http://74949mkfh190.vicp.fun', // 付
   // develop: 'http://47.109.84.152:8081',
   // develop: 'https://5ed0f7cc.r9.vip.cpolar.cn',
-  develop: 'https://smqjh.api.zswlgz.com',
+  // develop: 'https://smqjh.api.zswlgz.com',
   /**
    * 体验版
    */

+ 1 - 1
src/pages.json

@@ -472,7 +472,7 @@
         {
           "path": "chargeVoucher/chargeVoucher",
           "name": "charge-voucher",
-          "islogin": false,
+          "islogin": true,
           "style": {
             "navigationBarTitleText": "",
             "navigationStyle": "custom"

+ 64 - 7
src/subPack-charge/chargeBuyaTicketList/chargeBuyaTicketList.vue

@@ -1,4 +1,6 @@
 <script setup lang="ts">
+import { createGlobalLoadingMiddleware } from '@/api/core/middleware'
+
 definePage({
   name: 'charge-buy-a-ticket-list',
   islogin: false,
@@ -6,30 +8,85 @@ definePage({
     navigationBarTitleText: '购券记录',
   },
 })
+
+/**
+ * 获取购券记录列表
+ */
+const { data: recordList, isLastPage, page } = usePagination((pageNum, pageSize) =>
+  Apis.charge.getPurchaseRecordPage({ data: { pageNum, pageSize } }), {
+  data: (resp) => {
+    return resp.data?.list
+  },
+  initialData: [],
+  initialPage: 1,
+  initialPageSize: 10,
+  append: true,
+  immediate: true,
+  middleware: createGlobalLoadingMiddleware(),
+})
+
+onReachBottom(() => {
+  if (!isLastPage.value) {
+    page.value++
+  }
+})
+
+/**
+ * 获取状态文本
+ */
+function getStatusText(recordStatus?: number) {
+  switch (recordStatus) {
+    case 1:
+      return '已取消'
+    case 2:
+      return '已到账'
+    case 3:
+      return '退款'
+    default:
+      return '未知'
+  }
+}
+
+/**
+ * 获取状态样式
+ */
+function getStatusClass(recordStatus?: number) {
+  switch (recordStatus) {
+    case 1:
+      return 'bg-#AAAAAA text-#666666'
+    case 2:
+      return 'bg-#9ED605 text-#FFFFFF'
+    case 3:
+      return 'bg-#FF9500 text-#FFFFFF'
+    default:
+      return 'bg-#AAAAAA text-#666666'
+  }
+}
 </script>
 
 <template>
   <view class="min-h-screen bg-#F5F7FB px-24rpx">
     <view class="h-10rpx" />
-    <view v-for="item in 6" :key="item" class="mt-20rpx bg-#FFFFFF px-24rpx py-28rpx">
+    <StatusTip v-if="!recordList.length" tip="暂无内容" />
+    <view v-for="item in recordList" :key="item.recordNo" class="mt-20rpx bg-#FFFFFF px-24rpx py-28rpx">
       <view class="relative flex items-center justify-between">
         <view class="text-32rpx font-bold">
-          50元充电费抵扣券
+          {{ item.couponAmount }}元充电费抵扣券
         </view>
-        <view class="absolute right-[-24rpx] rounded-[30rpx_0rpx_0rpx_4rpx] px-14rpx py-8rpx text-28rpx text-#FFF" :class="item % 2 === 0 ? 'bg-#9ED605' : 'bg-#AAA'">
-          • 已到账
+        <view class="absolute right-[-24rpx] rounded-[30rpx_0rpx_0rpx_4rpx] px-14rpx py-8rpx text-28rpx text-#FFF" :class="getStatusClass(item.recordStatus)">
+          • {{ getStatusText(item.recordStatus) }}
         </view>
       </view>
       <view class="mt-28rpx h-2rpx w-full bg-#F0F0F0" />
       <view class="mt-28rpx">
         <view class="text-24rpx text-#AAAAAA">
-          订单编号:101121
+          订单编号:{{ item.recordNo }}
         </view>
         <view class="mt-20rpx text-24rpx text-#AAAAAA">
-          购券时间:2023-10-19022:25:22
+          购券时间:{{ item.createTime }}
         </view>
         <view class="mt-20rpx text-24rpx text-#AAAAAA">
-          实付金额:50元
+          实付金额:{{ item.couponAmount }}
         </view>
       </view>
     </view>

+ 19 - 3
src/subPack-charge/chargeOrderDetail/chargeOrderDetail.vue

@@ -158,10 +158,26 @@ async function getOrderDetail() {
         </view> -->
         <view class="mt-28rpx flex items-center justify-between">
           <view class="text-28rpx font-500">
-            实际结算费用
+            订单金额
           </view>
           <view class="text-28rpx text-#F44033">
-            {{ chargeOrderDetail?.realCost }}元
+            {{ chargeOrderDetail?.actualTotal || '--' }}元
+          </view>
+        </view>
+        <view class="mt-28rpx flex items-center justify-between">
+          <view class="text-24rpx">
+            积分扣减
+          </view>
+          <view class="text-24rpx text-#F44033">
+            {{ chargeOrderDetail?.realCost || '--' }}元
+          </view>
+        </view>
+        <view class="mt-28rpx flex items-center justify-between">
+          <view class="text-24rpx">
+            平台券
+          </view>
+          <view class="text-24rpx text-#F44033">
+            {{ chargeOrderDetail?.platformVolume || '--' }}元
           </view>
         </view>
       </view>
@@ -170,7 +186,7 @@ async function getOrderDetail() {
           !
         </view>
         <view class="text-24rpx">
-          本次充电费用已从您的“账户余额”中抵扣结算
+          本次充电费用已通过您的积分与市民券抵扣结算
         </view>
       </view>
     </view>

+ 77 - 7
src/subPack-charge/chargeVoucher/chargeVoucher.vue

@@ -2,28 +2,93 @@
 import router from '@/router'
 import { StaticUrl } from '@/config'
 
+const { userInfo } = storeToRefs(useUserStore())
 definePage({
   name: 'charge-voucher',
-  islogin: false,
+  islogin: true,
   style: {
     navigationBarTitleText: '',
     navigationStyle: 'custom',
   },
 })
 const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
+const couponBalance = ref()
+onLoad((options: any) => {
+  console.log(options, '路由参数')
+  couponBalance.value = options.couponBalance
+})
 onMounted(() => {
   opcity.value = 0
+  getRechargeLevels()
 })
 onPageScroll((e) => {
   const calculatedOpacity = e.scrollTop / 100
   opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
 })
 
-const selectIndex = ref(0)
+const selectIndex = ref<number>()
+
+/**
+ * 获取充值挡位
+ */
+const rechargeLevels = ref<Api.RechargeLevel[]>([])
+async function getRechargeLevels() {
+  const res = await Apis.charge.getReChargeLevel({})
+  rechargeLevels.value = res.data
+}
 function selectVoucher(index: number) {
   selectIndex.value = index
   console.log('Selected voucher index:', index)
 }
+
+// 安全获取充值档位
+function getSelectedLevel(): Api.RechargeLevel | undefined {
+  if (selectIndex.value === undefined || !rechargeLevels.value)
+    return undefined
+  return rechargeLevels.value[selectIndex.value]
+}
+
+/**
+ * 提交支付
+ */
+async function submitPay() {
+  if (selectIndex.value === undefined) {
+    return useGlobalMessage().alert('请选择充值金额')
+  }
+  const selectedLevel = getSelectedLevel()
+  if (!selectedLevel) {
+    return
+  }
+  try {
+    uni.showLoading({ title: '下单中...', mask: true })
+    const orderRes = await Apis.charge.addPurchaseRecord({
+      data: {
+        couponAmount: selectedLevel.money,
+        consigneeName: userInfo.value?.nickName || '',
+        consigneeMobile: userInfo.value?.mobile || '',
+      },
+    })
+    const orderNumber = orderRes.data
+    if (!orderNumber) {
+      uni.hideLoading()
+      uni.showToast({ title: '下单失败', icon: 'none' })
+      return
+    }
+    const payRes = await Apis.charge.wxJsApiPay({
+      data: {
+        orderNumber,
+      },
+    })
+    uni.hideLoading()
+    await useUserStore().getWxCommonPayment(payRes.data)
+    useUserStore().paySuccess('charge-buy-a-ticket-list', '/subPack-charge/chargeBuyaTicketList/chargeBuyaTicketList')
+  }
+  catch (error) {
+    uni.hideLoading()
+    console.error('支付失败:', error)
+    useUserStore().payError('charge-buy-a-ticket-list', '/subPack-charge/chargeBuyaTicketList/chargeBuyaTicketList')
+  }
+}
 </script>
 
 <template>
@@ -45,7 +110,8 @@ function selectVoucher(index: number) {
             平台券金额
           </view>
           <view class="mt-16rpx text-28rpx font-800">
-            100<text class="text-24rpx">
+            {{ couponBalance }}
+            <text class="text-24rpx">
             </text>
           </view>
@@ -65,14 +131,14 @@ function selectVoucher(index: number) {
           本次充值
         </view>
         <view class="mt-24rpx flex flex-wrap items-center justify-start gap-20rpx">
-          <view v-for="(item, index) in 6" :key="index" class="h-136rpx w-220rpx text-center" :class="selectIndex === index ? 'text-[#9ED605]' : 'text-#2B303Ac'" :style="{ backgroundImage: `url(${StaticUrl}/${selectIndex === index ? 'buy-center-rechargesel' : 'buy-center-recharge'}.png)`, backgroundSize: 'cover', backgroundPosition: 'center' }" @click="selectVoucher(index)">
+          <view v-for="(item, index) in rechargeLevels" :key="index" class="h-136rpx w-220rpx text-center" :class="selectIndex === index ? 'text-[#9ED605]' : 'text-#2B303Ac'" :style="{ backgroundImage: `url(${StaticUrl}/${selectIndex === index ? 'buy-center-rechargesel' : 'buy-center-recharge'}.png)`, backgroundSize: 'cover', backgroundPosition: 'center' }" @click="selectVoucher(index)">
             <view class="h-80rpx text-28rpx font-bold line-height-[80rpx]">
-              10<text class="text-24rpx">
+              {{ item?.money }}<text class="text-24rpx">
               </text>
             </view>
             <view class="h-46rpx text-28rpx font-bold line-height-[46rpx] font-italic">
-              充电优惠券
+              {{ item?.name }}
             </view>
           </view>
         </view>
@@ -94,7 +160,11 @@ function selectVoucher(index: number) {
           </view>
         </view>
       </view>
-      <view class="fixed bottom-66rpx left-24rpx h-100rpx w-702rpx rounded-16rpx bg-[linear-gradient(90deg,#DBFC81_0%,#9ED605_100%)] text-center text-28rpx font-800 line-height-[100rpx] shadow-[inset_0rpx_6rpx_20rpx_2rpx_#FFFFFF]">
+      <view
+        class="fixed bottom-66rpx left-24rpx h-100rpx w-702rpx rounded-16rpx text-center text-28rpx font-800 line-height-[100rpx]"
+        :class="selectIndex === undefined ? 'bg-[#CCCCCC] text-white' : 'bg-[linear-gradient(90deg,#DBFC81_0%,#9ED605_100%)] text-black shadow-[inset_0rpx_6rpx_20rpx_2rpx_#FFFFFF]'"
+        @click="submitPay"
+      >
         立即支付
       </view>
     </view>

+ 3 - 3
src/subPack-charge/index/index.vue

@@ -111,7 +111,7 @@ function handleFilterClick(filterKey: number) {
               </view>
             </view>
             <view class="mt-16rpx text-28rpx font-bold">
-              {{ userInfo?.mobile || '暂无手机号' }}
+              {{ userInfo?.mobile }}
             </view>
           </view>
         </view>
@@ -150,7 +150,7 @@ function handleFilterClick(filterKey: number) {
               <view class="mt-16rpx text-40rpx text-#9ED605 font-bold">
                 <text class="text-24rpx">
-                </text>5
+                </text>{{ userAccountInfo?.couponBalance || '0.00' }}
               </view>
             </view>
           </view>
@@ -179,7 +179,7 @@ function handleFilterClick(filterKey: number) {
             />
           </view>
         </view>
-        <view class="flex items-center gap-18rpx rounded-16rpx bg-#FFF px-24rpx py-28rpx" @click="router.push({ name: 'charge-voucher' })">
+        <view class="flex items-center gap-18rpx rounded-16rpx bg-#FFF px-24rpx py-28rpx" @click="router.push({ name: 'charge-voucher', params: { couponBalance: userAccountInfo?.couponBalance } })">
           <view>
             <view class="flex items-center gap-16rpx">
               <text class="text-32rpx font-bold">