Bladeren bron

feat(app): 设计师积分结账

EvilDragon 5 maanden geleden
bovenliggende
commit
8e33ac71da

+ 6 - 0
packages/app/src/core/libs/models.ts

@@ -547,3 +547,9 @@ export enum BannerMode {
    */
   StudyTour = 5,
 }
+export enum QrCodeBusinessType {
+  /**
+   * 积分结账
+   */
+  PointsCheckout = 'PC',
+}

+ 5 - 0
packages/app/src/core/libs/requests.ts

@@ -456,6 +456,11 @@ export const orderPay = (data: {
   totalsCouponPoints?: number
   totalsCurrPoints?: number
 }) => httpPost(`/app-api/member/points-order/placingPay`, data)
+/**
+ * 积分结账
+ */
+export const pointsPay = (data: { userId: number; points: number; vendorId: number }) =>
+  httpPost('/app-api/member/points-order/orderAndPaymentCompleted', data)
 export const getPointsOrders = (query) =>
   httpGet<{
     list: PointsOrder[]

+ 30 - 0
packages/app/src/core/utils/common.ts

@@ -78,3 +78,33 @@ export const getCountsArr = (data, imgWidth, imgHeight) => {
   b = Math.round(b)
   return [r, g, b].join(',')
 }
+/**
+ * 对象转二维码参数
+ */
+export const toQrCodeString = (type: string, options: Record<string, any>) => {
+  if (!options || typeof options !== 'object') {
+    return ''
+  }
+  let str = `${type}:`
+  for (const key in options) {
+    str += `${key}:${options[key]};`
+  }
+  return `${str};`
+}
+/**
+ * 二维码参数转对象
+ */
+export const qrCodeString2Object = (str: string): Record<string, any> => {
+  if (!str || typeof str !== 'string') {
+    return {}
+  }
+  const [type, ...options] = str.split(':')
+  const obj = {} as Record<string, any>
+  for (const item of options.join(':').split(';' as const)) {
+    const [key, value] = item.split(':')
+    if (key && value) {
+      obj[key] = value
+    }
+  }
+  return { type, options: obj }
+}

+ 32 - 0
packages/app/src/pages.json

@@ -353,6 +353,14 @@
       }
     },
     {
+      "path": "pages/common/status/success/index",
+      "type": "page",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationBarBackgroundColor": "#fff"
+      }
+    },
+    {
       "path": "pages/home/activity/detail/index",
       "type": "page",
       "style": {
@@ -454,6 +462,22 @@
       }
     },
     {
+      "path": "pages/mine/scan/error/index",
+      "type": "page",
+      "style": {
+        "navigationBarTitleText": "扫码结果",
+        "navigationBarBackgroundColor": "#fff"
+      }
+    },
+    {
+      "path": "pages/mine/scan/result/index",
+      "type": "page",
+      "style": {
+        "navigationBarTitleText": "扫码结果",
+        "navigationBarBackgroundColor": "#fff"
+      }
+    },
+    {
       "path": "pages/home/mall/purchased/success/index",
       "type": "page",
       "style": {
@@ -482,6 +506,14 @@
       "style": {
         "navigationBarTitleText": "提交成功"
       }
+    },
+    {
+      "path": "pages/mine/scan/pay/success/index",
+      "type": "page",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationBarBackgroundColor": "#fff"
+      }
     }
   ],
   "subPackages": []

+ 25 - 0
packages/app/src/pages/common/status/success/index.vue

@@ -0,0 +1,25 @@
+<route lang="json">
+{ "style": { "navigationBarTitleText": "", "navigationBarBackgroundColor": "#fff" } }
+</route>
+<script setup lang="ts">
+import { success } from '../../../../core/libs/svgs'
+const data = ref<{ title: string; navTitle: string; desc: string } | undefined>()
+onLoad(async (query: typeof data.value) => {
+  data.value = query
+  uni.setNavigationBarTitle({ title: data.value.navTitle || '' })
+})
+</script>
+<template>
+  <div class="flex-grow flex flex-col items-center justify-center">
+    <wd-img width="60" height="60" :src="success" custom-class="mb-4.5"></wd-img>
+    <div class="mb-10 text-black/90 text-xl font-normal font-['PingFang_SC'] leading-none">
+      {{ data?.title }}
+    </div>
+    <div
+      v-if="data?.desc"
+      class="text-black/40 text-base font-normal font-['PingFang SC'] leading-none"
+    >
+      {{ data?.desc }}
+    </div>
+  </div>
+</template>

+ 18 - 1
packages/app/src/pages/mine/index.vue

@@ -15,6 +15,8 @@ import { isEmpty } from 'radash'
 import TasksCard from './components/tasks-card.vue'
 import { useRouter } from '../../core/utils/router'
 import { NetImages } from '../../core/libs/net-images'
+import { qrCodeString2Object, toQrCodeString } from '../../core/utils/common'
+import { QrCodeBusinessType } from '@/core/libs/models'
 
 const router = useRouter()
 const userStore = useUserStore()
@@ -121,6 +123,15 @@ const handleToHomepage = () => {
 const handleMenuClick = (path) => {
   path && uni.navigateTo({ url: path })
 }
+const handleClickScan = async () => {
+  const { result } = await uni.scanCode({})
+  console.log(result)
+  const a = qrCodeString2Object('WIFI:S:KM;T:WPA;P:km666888;H:false;;')
+  console.log(a)
+  console.log(toQrCodeString('到店', { a: 1, orderId: 2222 }))
+  console.log(qrCodeString2Object(toQrCodeString('到店', { a: 1, orderId: 2222 })))
+  router.push(`/pages/mine/scan/result/index?result=${result}`)
+}
 onMounted(async () => {})
 const navBarProps = ref({ customClass: 'bg-transparent!' })
 onPageScroll(({ scrollTop }: { scrollTop: number }) => {
@@ -139,7 +150,13 @@ onPageScroll(({ scrollTop }: { scrollTop: number }) => {
       v-bind="navBarProps"
     >
       <template #left>
-        <wd-button type="text" size="small" custom-class="p-0!" :round="false">
+        <wd-button
+          type="text"
+          size="small"
+          custom-class="p-0!"
+          :round="false"
+          @click="handleClickScan"
+        >
           <wd-img width="25" height="25" :src="scan" custom-class="vertical-bottom"></wd-img>
         </wd-button>
       </template>

+ 19 - 0
packages/app/src/pages/mine/scan/error/index.vue

@@ -0,0 +1,19 @@
+<route lang="json">
+{ "style": { "navigationBarTitleText": "扫码结果", "navigationBarBackgroundColor": "#fff" } }
+</route>
+<script setup lang="ts">
+const msg = ref()
+onLoad(async (query: { msg: string }) => {
+  msg.value = query.msg
+})
+</script>
+<template>
+  <div class="flex-grow flex flex-col items-center justify-center">
+    <div class="text-black/60 text-base font-normal font-['PingFang_SC'] leading-none">
+      不支持此二维码/条码
+    </div>
+    <div class="text-black/60 text-base font-normal font-['PingFang_SC'] leading-none">
+      {{ msg }}
+    </div>
+  </div>
+</template>

+ 15 - 0
packages/app/src/pages/mine/scan/pay/success/index.vue

@@ -0,0 +1,15 @@
+<route lang="json">
+{ "style": { "navigationBarTitleText": "", "navigationBarBackgroundColor": "#fff" } }
+</route>
+<script setup lang="ts">
+import { success } from '@/core/libs/svgs'
+onLoad(async (query: { msg: string }) => {})
+</script>
+<template>
+  <div class="flex-grow flex flex-col items-center justify-center">
+    <wd-img width="60" height="60" :src="success" custom-class="mb-4.5"></wd-img>
+    <div class="mb-10 text-black/90 text-xl font-normal font-['PingFang_SC'] leading-none">
+      提交成功
+    </div>
+  </div>
+</template>

+ 90 - 0
packages/app/src/pages/mine/scan/result/index.vue

@@ -0,0 +1,90 @@
+<route lang="json">
+{ "style": { "navigationBarTitleText": "扫码结果", "navigationBarBackgroundColor": "#fff" } }
+</route>
+<script setup lang="ts">
+import { pointsPay } from '../../../../core/libs/requests'
+import { QrCodeBusinessType } from '../../../../core/libs/models'
+import { qrCodeString2Object, requestToast } from '../../../../core/utils/common'
+import { useUserStore } from '@/store'
+import { storeToRefs } from 'pinia'
+import { useRouter } from '../../../../core/utils/router'
+
+const router = useRouter()
+const userStore = useUserStore()
+const { userInfo } = storeToRefs(userStore)
+const msg = ref()
+const scanType = ref()
+const data = ref<{
+  id?: string
+  amount?: string
+  points?: string
+  avatar?: string
+  name?: string
+}>()
+const isPointsCheckout = computed(() => scanType.value === QrCodeBusinessType.PointsCheckout)
+const handleSubmit = async () => {
+  if (isPointsCheckout.value) {
+    const { code } = await requestToast(() =>
+      pointsPay({
+        vendorId: Number(data.value?.id),
+        userId: userInfo.value.userId,
+        points: Number(data.value?.points),
+      }),
+    )
+    if (code === 0) {
+      router.replace(`/pages/common/status/success/index?title=支付成功`)
+    }
+  }
+}
+onLoad(async (query: { result: string }) => {
+  try {
+    const { type, options } = qrCodeString2Object(query.result)
+    scanType.value = type
+    data.value = options
+    console.log(scanType.value)
+    console.log(data.value)
+
+    if (isPointsCheckout.value) uni.setNavigationBarTitle({ title: '付款' })
+  } catch (error) {
+    msg.value = error.message
+  }
+})
+</script>
+<template>
+  <div class="flex-grow flex flex-col items-center justify-center">
+    <template v-if="isPointsCheckout">
+      <div class="flex items-center">
+        <wd-img
+          class="rounded-full"
+          width="45"
+          height="45"
+          custom-class="border border-[#f2f2f2] border-solid"
+          :src="data?.avatar"
+        />
+        <div class="text-black/90 text-base font-normal font-['PingFang_SC'] leading-[10.18px]">
+          {{ data?.name }}
+        </div>
+      </div>
+      <div>
+        <div class="text-black/90 text-3xl font-normal font-['PingFang_SC'] leading-none">¥</div>
+        <div class="text-black/90 text-[50px] font-medium font-['DIN'] leading-none">
+          {{ data?.amount }}
+        </div>
+      </div>
+      <div>
+        <div class="text-black/40 text-base font-normal font-['PingFang_SC'] leading-none">
+          积分:{{ data?.points }}
+        </div>
+      </div>
+      <div><wd-button @click="handleSubmit">确认付款</wd-button></div>
+    </template>
+    <template v-else>
+      <div class="text-black/60 text-base font-normal font-['PingFang_SC'] leading-none">
+        不支持此二维码/条码
+      </div>
+      <div class="text-black/60 text-base font-normal font-['PingFang_SC'] leading-none">
+        {{ msg }}
+      </div>
+    </template>
+  </div>
+</template>

+ 5 - 1
packages/app/src/types/uni-pages.d.ts

@@ -39,6 +39,7 @@ interface NavigateToOptions {
        "/pages/mine/setting/index" |
        "/pages/publish/moment/index" |
        "/pages/publish/tags/index" |
+       "/pages/common/status/success/index" |
        "/pages/home/activity/detail/index" |
        "/pages/home/mall/confirm-order/index" |
        "/pages/home/mall/detail/index" |
@@ -52,10 +53,13 @@ interface NavigateToOptions {
        "/pages/mine/homepage/edit/index" |
        "/pages/mine/orders/code/index" |
        "/pages/mine/orders/detail/index" |
+       "/pages/mine/scan/error/index" |
+       "/pages/mine/scan/result/index" |
        "/pages/home/mall/purchased/success/index" |
        "/pages/home/spread/case-shooting/photographer/index" |
        "/pages/mine/authentication/submit/success/index" |
-       "/pages/mine/homepage/consult/success/index";
+       "/pages/mine/homepage/consult/success/index" |
+       "/pages/mine/scan/pay/success/index";
 }
 interface RedirectToOptions extends NavigateToOptions {}
 

+ 14 - 3
packages/merchant/src/pages/home/merchant/settlement.vue

@@ -4,16 +4,26 @@
 <script lang="ts" setup>
 import { ref } from 'vue'
 import UQRCode from 'uqrcodejs'
+import { toQrCodeString } from '@designer-hub/app/src/core/utils/common'
+import { QrCodeBusinessType } from '@designer-hub/app/src/core/libs/models'
+import { getVendorAppInfo } from '../../../core/libs/requests'
 
-const codeImageSrc = ref('')
 const orderAmount = ref()
 const pointsAmount = ref()
+const { data, run: setData } = useRequest(() => getVendorAppInfo())
 
-const generateQR = async (text) => {
+const generateQR = async () => {
   // 获取uQRCode实例
   const qr = new UQRCode()
   // 设置二维码内容
-  qr.data = `${orderAmount.value}|${pointsAmount.value}`
+  // qr.data = `${orderAmount.value}|${pointsAmount.value}`
+  qr.data = toQrCodeString(QrCodeBusinessType.PointsCheckout, {
+    id: data.value.id,
+    amount: orderAmount.value,
+    points: pointsAmount.value,
+    avatar: data.value.avatar.split('://')[1] || '',
+    name: data.value.vendorName,
+  })
   // 设置二维码大小,必须与canvas设置的宽高一致
   qr.size = 200
   // 调用制作二维码方法
@@ -28,6 +38,7 @@ const generateQR = async (text) => {
 onLoad(async (query: { orderAmount?: string; pointsAmount?: string }) => {
   orderAmount.value = query?.orderAmount || ''
   pointsAmount.value = query?.pointsAmount || ''
+  await setData()
   await generateQR()
 })
 </script>