Преглед изворни кода

feat(honor-dialog): 重构荣誉弹窗组件并添加新功能- 重构荣誉弹窗组件,支持徽章和证书两种类型- 添加查看奖励跳转功能- 优化弹窗样式和布局
- 新增徽章弹窗展示逻辑
- 修复荣誉弹窗相关的一些小问题

EvilDragon пре 2 месеци
родитељ
комит
9fcfe6d273

+ 0 - 20
packages/app/src/composables/honor-dialog.ts

@@ -1,20 +0,0 @@
-import { inject, InjectionKey } from 'vue'
-export interface DialogShowOptions {
-  title: string
-  content: string
-  path: string
-  image: string
-}
-export const HonorDialogSymbol: InjectionKey<{
-  show: (options: DialogShowOptions) => void
-}> = Symbol.for('HonorDialogContext')
-export const useHonorDialog = () => {
-  const honorDialog = inject(HonorDialogSymbol)
-  // if (!honorDialog) {
-  //   throw new Error('useHonorDialog must be used inside setup()')
-  // }
-  const show = computed(() => honorDialog?.show)
-  return {
-    show,
-  }
-}

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

@@ -1284,6 +1284,7 @@ export enum DictType {
  * 徽章
  */
 export interface Badge {
+  id: number
   userId: number
   quantity: number
   badgeId: number
@@ -1292,6 +1293,7 @@ export interface Badge {
   badgeNotObtainedImage: string
   badgeYesObtainedImage: string
   badgeDescription: string
+  popUp: boolean
 }
 /**
  * 证书

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

@@ -924,6 +924,11 @@ export const getPointsCoupons = (query) =>
 export const getMyStudyTours = (query = {}) =>
   httpGet<MyStudyTour[]>('/app-api/member/app-study-abroad/getSignUpStudyAbroad', query)
 /**
+ * /app-api/member/stylist-honor/updatePopUp 更新弹窗
+ */
+export const updateHonorPopUp = (query: { bizId: string; bizType: string }) =>
+  httpGet('/app-api/member/stylist-honor/updatePopUp', query)
+/**
  * 获取徽章列表
  */
 export const getBadges = (query = {}) =>

+ 1 - 0
packages/app/src/core/themes/default.ts

@@ -20,4 +20,5 @@ export const defaultThemeVars: ConfigProviderThemeVars = {
   inputNumberBtnWidth: '44rpx',
   inputNumberHeight: '44rpx',
   inputNumberIconSize: '20rpx',
+  overlayBg: 'rgba(0, 0, 0, 0.8)',
 }

+ 1 - 1
packages/app/src/layouts/tabbar.vue

@@ -15,7 +15,7 @@ import {
 import { currRoute } from '../utils'
 import { defaultThemeVars } from '../core/themes/default'
 import { useRouter } from '../core/utils/router'
-import HonorDialog from '@/pages/common/components/honor-dialog.vue'
+import HonorDialog from "@/pages/common/components/honor-dialog/honor-dialog.vue";
 
 const router = useRouter()
 const publishState = ref(false)

+ 45 - 12
packages/app/src/pages/common/components/honor-dialog.vue → packages/app/src/pages/common/components/honor-dialog/honor-dialog.vue

@@ -1,11 +1,15 @@
 <script setup lang="ts">
-import { close } from '../../../core/libs/svgs'
-import { DialogShowOptions, HonorDialogSymbol } from '../../../composables/honor-dialog'
+import { close } from '../../../../core/libs/svgs'
+import { HonorDialogOptions, HonorDialogSymbol } from '.'
 import earnBadgeTitle from '@designer-hub/assets/src/libs/assets/earnBadgeTitle'
 import radiation from '@designer-hub/assets/src/libs/assets/radiation'
-import { NetImages } from '../../../core/libs/net-images'
+import { NetImages } from '@/core/libs/net-images'
 import { ConfigProviderThemeVars } from 'wot-design-uni'
+import { useRouter } from '@/core/utils/router'
 
+const dialogOption = inject(HonorDialogSymbol, ref<HonorDialogOptions>({}))
+const { push } = useRouter()
+const lazyRender = ref<boolean>(true)
 const modelValue = defineModel({ default: false, type: Boolean })
 const themeVars: ConfigProviderThemeVars = {
   overlayBg: 'rgba(0,0,0,0.85)',
@@ -16,26 +20,54 @@ const path = ref('')
 const src = ref(
   'https://image.zhuchaohui.com/zhucaohui/e104215c64d39e4a0f8676c48b8e7221c891eade1c8d7f02b2a7f0be862e3f76.png',
 )
-const show = (options: DialogShowOptions) => {
-  title.value = options.title
-  content.value = options.content
-  path.value = options.path
-  src.value = options.image
-  modelValue.value = true
+const reset = (option) => {
+  if (option) {
+    modelValue.value = option.show
+    lazyRender.value = option.lazyRender
+    title.value = option.title || '东方研习营'
+    content.value = option.content || '获得东方研习营游学徽章'
+    path.value = option.path || ''
+    src.value = option.image || src.value
+  }
 }
-provide(HonorDialogSymbol, { show })
+const jumpTo = () => {
+  if (dialogOption.value?.type && dialogOption.value?.type === 'certificate') {
+    push('/pages/mine/honors/index?active=certificate')
+  }
+  if (dialogOption.value?.type && dialogOption.value?.type === 'badge') {
+    push('/pages/mine/honors/index?active=badge')
+  }
+}
+const handleLoad = () => {
+  console.log(1111)
+  if (dialogOption.value?.onLoad && typeof dialogOption.value?.onLoad === 'function') {
+    dialogOption.value.onLoad()
+  }
+}
+watch(
+  () => dialogOption.value,
+  (newVal) => {
+    reset(newVal)
+  },
+)
+// provide(HonorDialogSymbol, { show })
 </script>
 <template>
   <wd-config-provider :themeVars="themeVars">
-    <wd-popup v-model="modelValue" custom-class="bg-transparent! bg-[#f6f6f6]!">
+    <wd-popup
+      v-model="modelValue"
+      :lazy-render="lazyRender"
+      custom-class="bg-transparent! bg-[#f6f6f6]!"
+    >
       <div class="flex flex-col items-center relative">
         <wd-img width="60vw" mode="widthFix" :src="earnBadgeTitle"></wd-img>
         <div class="w-[100vw] h-[68vw] pt-2 flex">
           <div class="w-100vw h-100vw absolute left-0 right-0 top--8">
             <wd-img
-              custom-class="absolute! vertical-bottom"
+              custom-class="absolute! top-50% left-50% translate-[-50%,-50%] vertical-bottom"
               width="68%"
               height="68%"
+              @load="handleLoad"
               :src="radiation"
             ></wd-img>
             <wd-img
@@ -71,6 +103,7 @@ provide(HonorDialogSymbol, { show })
         >
           <div
             class="text-center text-[#c7bdab] text-base font-normal font-['PingFang_SC'] leading-normal"
+            @click.stop="jumpTo"
           >
             查看奖励
           </div>

+ 59 - 0
packages/app/src/pages/common/components/honor-dialog/index.ts

@@ -0,0 +1,59 @@
+export interface DialogShowOptions {
+  title: string
+  content: string
+  path: string
+  image: string
+}
+export interface HonorDialogOptions {
+  title?: string
+  content?: string
+  image?: string
+  type?: 'badge' | 'certificate'
+  onLoad?: () => void
+}
+// export const HonorDialogSymbol: InjectionKey<{
+//   show: (options: DialogShowOptions) => void
+// }> = Symbol.for('HonorDialogContext')
+export const HonorDialogSymbol = Symbol.for('HonorDialogContext')
+// export const useHonorDialog = () => {
+//   const honorDialog = inject(HonorDialogSymbol)
+//   // if (!honorDialog) {
+//   //   throw new Error('useHonorDialog must be used inside setup()')
+//   // }
+//   const show = computed(() => honorDialog?.show)
+//   return {
+//     show,
+//   }
+// }
+export const useHonorDialog = () => {
+  const dialogOption = ref({})
+  // inject(HonorDialogSymbol, dialogOption)
+  // const honorDialog = inject(HonorDialogSymbol)
+  // console.log(honorDialog)
+  provide(HonorDialogSymbol, dialogOption)
+  // if (!honorDialog) {
+  //   throw new Error('useHonorDialog must be used inside setup()')
+  // }
+  const show = (option: HonorDialogOptions) => {
+    return new Promise((resolve, reject) => {
+      const options = {
+        ...option,
+      }
+      dialogOption.value = {
+        ...options,
+        ...{
+          show: true,
+          onConfirm: (res) => {
+            resolve(res)
+          },
+          onCancel: (res) => {
+            reject(res)
+          },
+        },
+      }
+    })
+  }
+  return {
+    show,
+  }
+}

+ 24 - 2
packages/app/src/pages/home/index.vue

@@ -18,11 +18,13 @@ import useRequest from '../../hooks/useRequest'
 import Menus from './components/menus.vue'
 import {
   getActivities,
+  getBadges,
   getCircles,
   getMyStudyTours,
   getSetIndexConfigs,
   getStudyTours,
   shareCircle,
+  updateHonorPopUp,
   updateSetIndexConfig,
 } from '../../core/libs/requests'
 import { logo } from '../../core/libs/svgs'
@@ -38,9 +40,9 @@ import { pick, sort } from 'radash'
 import { Activity, StudyTour } from '../../core/libs/models'
 import PageHelperEvo from '@/components/page-helper-evo.vue'
 import { useMessage } from 'wot-design-uni'
-import { useHonorDialog } from '../../composables/honor-dialog'
 import ShareActionSheet from '@/pages/common/components/share-action-sheet.vue'
 import { useShare } from '@/composables/share'
+import { useHonorDialog } from '@/pages/common/components/honor-dialog'
 
 defineOptions({
   name: 'Home',
@@ -126,7 +128,27 @@ onLoad(async () => {
   swiperData.value = indexConfigsData.value.list.map((it) => ({
     data: it,
   }))
-  show.value({ title: '看不见', content: '看不见', path: '', image: '' })
+  const { data: badgeData } = await getBadges({})
+  const badges = Object.values(badgeData)
+    .flat()
+    .filter((it) => !it.popUp && it.quantity)
+  console.log(badges)
+  // const option = {};
+  if (badges.length) {
+    await show({
+      title: badges[0].badgeName,
+      content: badges[0].badgeDescription,
+      image: badges[0].badgeYesObtainedImage,
+      type: 'badge',
+      onLoad: async () => {
+        // await updateHonorPopUp({ bizId: String(badges[0].id), bizType: '1' })
+      },
+    })
+  }
+  // await show({
+  //   title: badges
+  // })
+  // show.value({ title: '看不见', content: '看不见', path: '', image: '' })
 })
 onHide(() => {
   // autoplay.value = true

+ 19 - 30
packages/app/src/pages/home/mall/shopping-cart/index.vue

@@ -11,8 +11,6 @@
 import TiltedButton from '@/components/tilted-button.vue'
 import Product from '../components/product.vue'
 import { shoppingBag } from '@designer-hub/assets/src/assets/svgs/index'
-import InvertedTrapezoidButton from '@/components/inverted-trapezoid-button.vue'
-import TrapeziumButton from '@/components/trapezium-button.vue'
 import {
   createProductItemBuy,
   deleteProductItemBuy,
@@ -107,35 +105,26 @@ const handlePlaceOrder = async () => {
     await uni.showToast({ title: '请选择商品', icon: 'none' })
     return ''
   }
-  const { code, data: res } = await requestToast(() =>
-    productPlacing({
-      isShoppingCart: 1,
-      userId: userInfo.value.userId,
-      item: 3,
-      list: selected.value.map(
-        ({ productId, prodcutName, productCoverImgUrl, nums, points, vendorId }) => ({
-          productId,
-          productName: prodcutName,
-          orderImgUrl: productCoverImgUrl,
-          nums,
-          points,
-          vendorId,
-        }),
-      ),
-      couponList: [],
-    }),
-  )
+  const body = {
+    isShoppingCart: 1,
+    userId: userInfo.value.userId,
+    item: 3,
+    list: selected.value.map(
+      ({ productId, prodcutName, productCoverImgUrl, nums, points, vendorId }) => ({
+        productId,
+        productName: prodcutName,
+        orderImgUrl: productCoverImgUrl,
+        nums,
+        points,
+        vendorId,
+      }),
+    ),
+    couponList: [],
+  }
+  const { code, data: res } = await requestToast(() => productPlacing(body))
   if (code === 0) {
-    // router.push(`/pages/home/mall/confirm-order`)
-    // await deleteProductItemBuy({
-    //   doList: selected.value.map(({ productId }) => ({
-    //     productId,
-    //     deleted: true,
-    //     userId: userInfo.value.userId,
-    //   })),
-    // })
-    pageHelperRef.value?.reload()
-    router.push(`/pages/home/mall/confirm-order/index?data=${JSON.stringify(res)}`)
+    await pageHelperRef.value?.reload()
+    await router.push(`/pages/home/mall/confirm-order/index?data=${JSON.stringify(body)}`)
   }
 }
 </script>

+ 19 - 19
packages/app/src/pages/home/spread/product-detail/index.vue

@@ -35,26 +35,26 @@ const { data, run: setData } = useRequest(() => getProduct(id.value))
 
 const handleConfirm = async () => {
   if (type.value === 'orderNow') {
-    const { data: res, code } = await requestToast(() =>
-      productPlacing({
-        isShoppingCart: 0,
-        userId: userInfo.value.userId,
-        item: item.value,
-        list: [
-          {
-            productId: id.value,
-            points: data.value.points,
-            nums: 1,
-            productName: data.value.prodcutName,
-            orderImgUrl: data.value.productCoverImgUrl,
-            vendorId: data.value.vendorId,
-          },
-        ],
-        couponList: [],
-      }),
-    )
+    const body = {
+      isShoppingCart: 0,
+      userId: userInfo.value.userId,
+      item: item.value,
+      list: [
+        {
+          productId: id.value,
+          points: data.value.points,
+          nums: 1,
+          productName: data.value.prodcutName,
+          orderImgUrl: data.value.productCoverImgUrl,
+          vendorId: data.value.vendorId,
+        },
+      ],
+      couponList: [],
+    }
+    const { data: res, code } = await requestToast(() => productPlacing(body))
     if (code !== 0) return
-    router.push(`/pages/home/mall/confirm-order/index?data=${JSON.stringify(res)}`)
+    // router.push(`/pages/home/mall/confirm-order/index?data=${JSON.stringify(res)}`)
+    await router.push(`/pages/home/mall/confirm-order/index?data=${JSON.stringify(body)}`)
   }
 }
 onLoad(async (query: { id: string; title: string; item: string }) => {

+ 42 - 10
packages/app/src/pages/mine/honors/index.vue

@@ -15,6 +15,8 @@ import { useRouter } from '../../../core/utils/router'
 import arcBottom from '@designer-hub/assets/src/libs/assets/arcBottom'
 import { NetImages } from '../../../core/libs/net-images'
 import ProgressEvo from '@/components/progress-evo.vue'
+import { Certificate } from '@/core/libs/models'
+import dayjs from 'dayjs'
 
 const router = useRouter()
 const active = ref('badge')
@@ -29,11 +31,16 @@ const { data: badges, run: setBadges } = useRequest(() => getBadges({}), {
 const { data: certificates, run: setCertificates } = useRequest(() => getCertificates({}), {
   initialData: [],
 })
-onMounted(async () => {
+const currentCertificate = ref<Certificate | undefined>()
+onLoad(async (query?: Record<string | 'active', string>) => {
+  if (query?.active) {
+    active.value = query.active
+  }
   await setStatistics()
   await setBadges()
   await setCertificates()
-  console.log(badges.value)
+  // currentCertificate.value = certificates.value[0]
+  // console.log(badges.value)
 })
 </script>
 <template>
@@ -199,18 +206,43 @@ onMounted(async () => {
       <div>
         <div class="grid grid-cols-2 gap-2.5">
           <template v-for="(it, i) in certificates" :key="i">
-            <div
-              @click="
-                router.push(
-                  `/pages/mine/honors/detail/index?type=certificate&data=${JSON.stringify(it)}`,
-                )
-              "
-            >
-              <wd-img width="100%" :src="it.certificateImage" mode="widthFix"></wd-img>
+            <div class="aspect-[1.35/1]" @click="currentCertificate = it">
+              <wd-img
+                width="100%"
+                height="100%"
+                :src="it.certificateImage"
+                mode="aspectFill"
+              ></wd-img>
             </div>
           </template>
         </div>
       </div>
     </template>
+    <wd-overlay :show="currentCertificate !== undefined" @click="currentCertificate = undefined">
+      <div class="h-full flex flex-col items-center justify-around">
+        <div class="flex flex-col">
+          <wd-img
+            width="85vw"
+            height="94vw"
+            :src="currentCertificate?.certificateImage"
+            mode="aspectFit"
+          ></wd-img>
+          <div
+            class="text-center text-white text-xl font-normal font-['PingFang SC'] uppercase mt-7"
+          >
+            {{ currentCertificate?.certificateName }}
+          </div>
+          <div class="flex center gap-3">
+            <div class="w-3.5 h-[1.5px] bg-white"></div>
+            <div class="text-center text-white text-sm font-normal font-['PingFang SC'] uppercase">
+              <!--          2024年10月2日获得-->
+              {{ dayjs(currentCertificate?.createTime).format('YYYY年MM月DD日') }}获得
+            </div>
+            <div class="w-3.5 h-[1.5px] bg-white"></div>
+          </div>
+        </div>
+        <wd-button custom-class="bg-[#0CBE7D]!">去分享</wd-button>
+      </div>
+    </wd-overlay>
   </div>
 </template>