Browse Source

feat(app): 优化个人主页并添加二维码功能

- 移除未使用的 shareCircle 函数
- 优化荣誉弹窗逻辑,支持徽章和证书的区分
- 添加二维码生成和扫描功能
- 更新个人主页界面,优化头像和背景图显示
- 调整统计数据展示,修复数据类型错误
- 新增个人码功能,支持设计师生成和保存个人二维码
EvilDragon 2 months ago
parent
commit
5d546981fb

+ 2 - 0
packages/app/package.json

@@ -98,6 +98,8 @@
     "@dcloudio/uni-quickapp-webview": "3.0.0-alpha-4020820240920001",
     "@designer-hub/assets": "workspace:*",
     "@huolala-tech/page-spy-uniapp": "^1.9.9",
+    "@uqrcode/js": "^4.0.7",
+    "@uqrcode/uni-app": "^4.0.7",
     "czg": "^1.9.3",
     "dayjs": "1.11.10",
     "extract-colors": "^4.1.0",

+ 1 - 0
packages/app/src/composables/permissions.ts

@@ -65,6 +65,7 @@ export const usePermissions = () => {
     toDesignerHomePage: isLogined.value,
     checkInAtStoreTask: isDesigner.value,
     shareMoment: isLogined.value && isDesigner.value && userInfo.value.level.level > 1,
+    personalCode: isLogined.value && isDesigner.value && userInfo.value.level.level > 1,
     /**
      * 微信代运营兑换
      */

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

@@ -1307,6 +1307,7 @@ export interface Certificate {
   certificateImage: string
   certificateDescription: string
   createTime: string
+  popUp: boolean
 }
 export enum CircleType {
   moment = '1',

+ 1 - 1
packages/app/src/core/libs/net-images.ts

@@ -2,7 +2,7 @@ export enum NetImages {
   'default' = 'https://cdn.jsdelivr.net/gh/yangyang-yangyang/yangyang-yangyang.github.io@master/images/default.png',
   'avatar' = 'https://cdn.jsdelivr.net/gh/yangyang-yangyang/yangyang-yangyang.github.io@master/images/avatar.png',
   'NotContent' = 'https://image.zhuchaohui.com/zhucaohui/3819d411440c23cc9e4f4bd3a520325386d7f038ed6dfa7c2ba076bd5110d2d2.png',
-  'DesigerHomepageDefaultBg' = 'https://image.zhuchaohui.com/zhucaohui/58dcb982d2957c5578478abbf000936efe9d11c96c5af4d457177cf5d90a9d39.png',
+  DesignerHomepageDefaultBg = 'https://image.zhuchaohui.com/zhucaohui/c706ec14a5a927c10e9e1fa0153affb11bbdd9255882e18c67ee82687ff9813a.png',
   ConsultDefaultBg = 'https://image.zhuchaohui.com/zhucaohui/7e98b5995902cd9484a6baecfc1219420cbcc30d8ae11e058af0c140a3c11137.png',
   StudyTourItemBg = 'https://image.zhuchaohui.com/zhucaohui/254b7ea7fdecaba8e318a7f50e292d036cafe49904fc7fc160a5477ce921e261.png',
   DefaultAvatar = 'https://image.zhuchaohui.com/zhucaohui/0b57771c2fbe60157e592a5b0e51a2b2b6c5263300663ad33efd55b235a2402a.png',

+ 15 - 10
packages/app/src/pages/home/index.vue

@@ -24,7 +24,6 @@ import {
   getMyStudyTours,
   getSetIndexConfigs,
   getStudyTours,
-  shareCircle,
   updateHonorPopUp,
   updateSetIndexConfig,
 } from '../../core/libs/requests'
@@ -32,8 +31,7 @@ import { logo } from '../../core/libs/svgs'
 import { ComponentExposed } from 'vue-component-type-helpers'
 import { usePermissions } from '../../composables/permissions'
 import { storeToRefs } from 'pinia'
-import { messages } from '../../core/libs/messages'
-import { handleUpvoteClick, handleShareClick } from '../../core/libs/actions'
+import { handleUpvoteClick } from '../../core/libs/actions'
 import { useUserStore } from '../../store'
 import ScheduleCard from './components/schedule-card.vue'
 import dayjs from 'dayjs'
@@ -138,16 +136,20 @@ onLoad(async () => {
   const honors = [
     ...badges.map((it) => ({
       type: HonorDialogType.Badge,
+      id: it.id,
       title: it.badgeName,
       content: it.badgeDescription,
       image: it.badgeYesObtainedImage,
     })),
-    ...certificates.map((it) => ({
-      type: HonorDialogType.Certificate,
-      title: it.certificateName,
-      content: it.certificateDescription,
-      image: it.certificateImage,
-    })),
+    ...certificates
+      .filter((it) => !it.popUp)
+      .map((it) => ({
+        type: HonorDialogType.Certificate,
+        id: it.id,
+        title: it.certificateName,
+        content: it.certificateDescription,
+        image: it.certificateImage,
+      })),
   ]
   // console.log(honors)
   // console.log(badges)
@@ -159,7 +161,10 @@ onLoad(async () => {
       image: honor.image,
       type: honor.type,
       onLoad: async () => {
-        await updateHonorPopUp({ bizId: String(badges[0].id), bizType: '1' })
+        await updateHonorPopUp({
+          bizId: String(honor.id),
+          bizType: honor.type === HonorDialogType.Badge ? '1' : '2',
+        })
       },
     })
   }

+ 4 - 3
packages/app/src/pages/mine/homepage/index.vue

@@ -175,7 +175,7 @@ defineExpose({
       <!-- <wd-img width="100%" custom-class="aspect-[1.14/1]" /> -->
       <div class="aspect-[1.14/1]">
         <ImageEvo
-          :src="designerInfo?.homePageUrl || NetImages.DesigerHomepageDefaultBg"
+          :src="designerInfo?.homePageUrl || NetImages.DesignerHomepageDefaultBg"
           mode="aspectFill"
         ></ImageEvo>
       </div>
@@ -186,7 +186,7 @@ defineExpose({
               <wd-img
                 width="100%"
                 height="100%"
-                :src="isOwn ? userInfo?.avatar : memberInfo?.avatar || NetImages.DefaultAvatar"
+                :src="memberInfo?.avatar || NetImages.DefaultAvatar"
               ></wd-img>
             </div>
             <div class="pb-8 flex-1">
@@ -195,6 +195,7 @@ defineExpose({
                   {{ designerInfo.homePageName || memberInfo.nickname }}
                 </div>
                 <div
+                  v-if="isOwn && features.personalCode"
                   class="flex items-center"
                   @click="router.push(`/pages/mine/homepage/qr-code/index`)"
                 >
@@ -243,7 +244,7 @@ defineExpose({
         {{ designerInfo?.designDesc }}
       </div>
 
-      <div class="h-[42px] relative mr--3.5">
+      <div v-if="badges?.length" class="h-[42px] relative mr--3.5">
         <div
           class="h-full left-0 pl-20 pr-6 right-20 top-0 bottom-0 absolute bg-gradient-to-r from-[#ffe9e9] via-[#fff7f7] to-[#fff8f8] rounded-tl-md rounded-bl-md flex flex-col justify-center"
         >

+ 84 - 14
packages/app/src/pages/mine/homepage/qr-code/index.vue

@@ -2,20 +2,22 @@
 { "style": { "navigationBarTitleText": "个人码", "navigationBarBackgroundColor": "#fff" } }
 </route>
 <script setup lang="ts">
-import dayjs from 'dayjs'
 import UQRCode from 'uqrcodejs'
-import { getPointsOrder } from '../../../../core/libs/requests'
-import { toQrCodeString } from '../../../../core/utils/common'
+import { getPointsOrder, storeAndPunchIn } from '../../../../core/libs/requests'
+import { qrCodeString2Object, toQrCodeString } from '../../../../core/utils/common'
 import { QrCodeBusinessType } from '../../../../core/libs/enums'
 import { useUserStore } from '../../../../store'
 import { storeToRefs } from 'pinia'
+import { useRouter } from '@/core/utils/router'
 
 const id = ref()
 const userStore = useUserStore()
 const { userInfo } = storeToRefs(userStore)
+const router = useRouter()
+const qrCodeCanvas = ref()
 const { data, run: setData } = useRequest(() => getPointsOrder(id.value), { initialData: {} })
 
-const a = (canvasContext: UniApp.CanvasContext) => {
+const a = async (canvasContext: UniApp.CanvasContext) => {
   const qr = new UQRCode()
   // 设置二维码内容
   // qr.data = data.value.orderNo
@@ -29,42 +31,108 @@ const a = (canvasContext: UniApp.CanvasContext) => {
   // 设置二维码大小,必须与canvas设置的宽高一致
   qr.size = 200
   qr.foregroundImageSrc = userInfo.value?.avatar
+  // console.log(userInfo.value?.avatar)
   // 调用制作二维码方法
   qr.make()
   qr.canvasContext = canvasContext
   // 调用绘制方法将二维码图案绘制到canvas上
-  qr.drawCanvas()
+  await qr.drawCanvas()
 }
 const generateCode = async () => {
-  nextTick(async () => {
-    const currentInstance = getCurrentInstance()
+  const currentInstance = getCurrentInstance()
+  await nextTick(async () => {
     const canvasContext = uni.createCanvasContext('qrcode', currentInstance) // 如果是组件,this必须传入
     a(canvasContext)
   })
 }
 const generateCodeMp = async () => {
   const currentInstance = getCurrentInstance()
-  nextTick(() => {
+  await nextTick(() => {
     uni
       .createSelectorQuery()
       .in(currentInstance)
       .select('#qrcode')
-      .fields({ node: true, size: true }, (res) => {
-        console.log(res)
-      })
+      .fields({ node: true, size: true }, () => {})
       .exec((res) => {
         const canvas = res[0].node
-        canvas.width = 200
-        canvas.height = 200
+        const dpr = wx.getSystemInfoSync().pixelRatio
+        canvas.width = res[0].width * dpr
+        canvas.height = res[0].height * dpr
         const ctx = canvas.getContext('2d')
+        ctx.scale(dpr, dpr)
+        UQRCode.prototype.loadImage = (src: string) =>
+          new Promise((resolve, reject) => {
+            const img = canvas.createImage()
+            img.src = src
+            img.onload = () => {
+              resolve(img)
+            }
+            img.onerror = (err) => {
+              // reject返回错误信息
+              reject(err)
+            }
+          })
         a(ctx)
+        qrCodeCanvas.value = canvas
       })
   })
 }
 
+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 })))
+  const { type, options } = qrCodeString2Object(result)
+  // if (type === QrCodeBusinessType.InStoreClockIn) {
+  //   if (!features.value.checkInAtStoreTask) return router.push('/pages/mine/authentication/index')
+  //   try {
+  //     await storeAndPunchIn({ id: options.id })
+  //     // await storeAndPunchIn({ id: 24 })
+  //     router.push(`/pages/mine/scan/result/index?result=${result}`)
+  //   } catch (e) {
+  //     if (e.code === 1000) {
+  //       router.push(
+  //         `/pages/mine/scan/result/index?result=${toQrCodeString(type, { name: options.name, desc: e.msg })}`,
+  //       )
+  //     } else {
+  //       uni.showToast({ title: e.msg, icon: 'none' })
+  //     }
+  //   }
+  //   return
+  // }
+  if (type === QrCodeBusinessType.PagePath) {
+    await router.push(options.path)
+    return
+  }
+  await router.push(`/pages/mine/scan/result/index?result=${result}`)
+}
+const saveQrcode = async () => {
+  try {
+    console.log(qrCodeCanvas.value)
+    const option = {
+      // #ifdef H5
+      canvasId: 'qrcode',
+      // #endif
+      // #ifdef MP-WEIXIN
+      canvas: toRaw(qrCodeCanvas.value),
+      // #endif
+    }
+    console.log(option)
+    const { tempFilePath } = await wx.canvasToTempFilePath(option)
+    console.log(tempFilePath)
+    await uni.saveImageToPhotosAlbum({
+      filePath: tempFilePath,
+    })
+    await uni.showToast({ title: '保存成功' })
+  } catch (e) {
+    await uni.showToast({ title: '保存失败', icon: 'none' })
+  }
+}
 onLoad(async (query: { id: string }) => {
   id.value = query.id
-  //   await setData()
   // #ifdef MP-WEIXIN
   await generateCodeMp()
   // #endif
@@ -98,6 +166,7 @@ onReady(() => {})
     </div>
     <div class="w-full flex items-center justify-around my-10">
       <div
+        @click="handleClickScan"
         class="text-center text-[#3f588f] text-base font-normal font-['PingFang_SC'] leading-normal"
       >
         扫一扫
@@ -109,6 +178,7 @@ onReady(() => {})
       </div>
       <div
         class="text-center text-[#3f588f] text-base font-normal font-['PingFang_SC'] leading-normal"
+        @click="saveQrcode"
       >
         保存图片
       </div>

+ 3 - 3
packages/app/src/pages/mine/homepage/statistics/index.vue

@@ -57,9 +57,9 @@ const setData = async ({ value }) => {
           code: res.code,
           msg: res.msg,
           data: {
-            shareCount: res.data.find((it) => it.bizType === '1')?.quantity || 0,
-            viewCount: res.data.find((it) => it.bizType === '2')?.quantity || 0,
-            winCustomerCount: res.data.find((it) => it.bizType === '3')?.quantity || 0,
+            shareCount: res.data.find((it) => String(it.bizType) === '1')?.quantity || 0,
+            viewCount: res.data.find((it) => String(it.bizType) === '3')?.quantity || 0,
+            winCustomerCount: res.data.find((it) => String(it.bizType) === '2')?.quantity || 0,
           },
         }),
       ),

+ 16 - 0
pnpm-lock.yaml

@@ -72,6 +72,12 @@ importers:
       '@huolala-tech/page-spy-uniapp':
         specifier: ^1.9.9
         version: 1.9.10
+      '@uqrcode/js':
+        specifier: ^4.0.7
+        version: 4.0.7
+      '@uqrcode/uni-app':
+        specifier: ^4.0.7
+        version: 4.0.7
       czg:
         specifier: ^1.9.3
         version: 1.11.0
@@ -3277,6 +3283,12 @@ packages:
     peerDependencies:
       vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0
 
+  '@uqrcode/js@4.0.7':
+    resolution: {integrity: sha512-4lIVp0mBrZw3yltc3C0/JuxMyNcqh7olX95xQYttrotITAxCL1AA0/RdcsBTSMP1M6TcKvJSuDDNUurDKyNuNw==}
+
+  '@uqrcode/uni-app@4.0.7':
+    resolution: {integrity: sha512-vVmbDrSHyeOeT7kmkzY7DzTPmwRDqceHUahg3DLmchNkEsSJ960RrKVXDPcy0olEn7q8/D/E7xzwOQKm8r61Ow==}
+
   '@vitejs/plugin-legacy@5.3.2':
     resolution: {integrity: sha512-8moCOrIMaZ/Rjln0Q6GsH6s8fAt1JOI3k8nmfX4tXUxE5KAExVctSyOBk+A25GClsdSWqIk2yaUthH3KJ2X4tg==}
     engines: {node: ^18.0.0 || >=20.0.0}
@@ -12887,6 +12899,10 @@ snapshots:
     transitivePeerDependencies:
       - rollup
 
+  '@uqrcode/js@4.0.7': {}
+
+  '@uqrcode/uni-app@4.0.7': {}
+
   '@vitejs/plugin-legacy@5.3.2(terser@5.37.0)(vite@5.2.8(@types/node@20.17.9)(less@4.2.1)(sass@1.82.0)(terser@5.37.0))':
     dependencies:
       '@babel/core': 7.26.0