Browse Source

feat(mine): 添加邀请设计师功能并优化个人主页分享

- 新增邀请设计师页面,实现视频号关联和海报生成功能
- 优化个人主页分享,添加自定义标题和图片
- 调整积分兑换和付款流程相关页面配置
EvilDragon 4 months ago
parent
commit
b9736dcb05

+ 1 - 0
packages/app/pages.config.ts

@@ -61,6 +61,7 @@ export default defineUniPages({
       },
       { name: '商品兑换成功', path: '/pages/home/mall/purchased/success/index' },
       { name: '购物车', path: '/pages/home/mall/shopping-cart/index' },
+      { name: '支付成功', path: '/pages/mine/scan/pay/success/index' },
     ],
   },
 })

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

@@ -6,4 +6,6 @@ export enum NetImages {
   ConsultDefaultBg = 'https://image.zhuchaohui.com/zhucaohui/7e98b5995902cd9484a6baecfc1219420cbcc30d8ae11e058af0c140a3c11137.png',
   StudyTourItemBg = 'https://image.zhuchaohui.com/zhucaohui/254b7ea7fdecaba8e318a7f50e292d036cafe49904fc7fc160a5477ce921e261.png',
   DefaultAvatar = 'https://image.zhuchaohui.com/zhucaohui/0b57771c2fbe60157e592a5b0e51a2b2b6c5263300663ad33efd55b235a2402a.png',
+  InviteBg = 'https://image.zhuchaohui.com/zhucaohui/b8f5350f9c09c1210adfa1417aaba4160b583928765df14194be2227ee928305.png',
+  Logo = 'https://image.zhuchaohui.com/zhucaohui/186acb6fdb8c8499c5ea77af317bfdab426edea94a009f8575877ab5179092d0.png',
 }

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

@@ -77,6 +77,10 @@
       {
         "name": "购物车",
         "path": "/pages/home/mall/shopping-cart/index"
+      },
+      {
+        "name": "支付成功",
+        "path": "/pages/mine/scan/pay/success/index"
       }
     ]
   },
@@ -308,6 +312,14 @@
       }
     },
     {
+      "path": "pages/mine/invite/index",
+      "type": "page",
+      "style": {
+        "navigationBarTitleText": "邀请设计师",
+        "navigationBarBackgroundColor": "#fff"
+      }
+    },
+    {
       "path": "pages/mine/levels/index",
       "type": "page",
       "style": {

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

@@ -138,7 +138,11 @@ onUnload(async () => {
     })
   }
 })
-onShareAppMessage(() => ({ title: `${userInfo.value.nickname}` }))
+onShareAppMessage(() => ({
+  title: `${userInfo.value.nickname}: “${designerInfo.value.designDesc}”`,
+  imageUrl: designerInfo.value?.homePageUrl,
+}))
+onShareTimeline(() => ({}))
 defineExpose({
   navBarFixed: false,
 })

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

@@ -53,7 +53,7 @@ const { data: taskStatusData, run: setTaskStatus } = useRequest(
       {
         btnProps: {
           content: '去邀请',
-          onClick: () => uni.showToast({ title: '敬请期待', icon: 'none' }),
+          onClick: () => router.push('/pages/mine/invite/index'),
         },
       },
       {

+ 192 - 0
packages/app/src/pages/mine/invite/index.vue

@@ -0,0 +1,192 @@
+<route lang="json">
+{ "style": { "navigationBarTitleText": "邀请设计师", "navigationBarBackgroundColor": "#fff" } }
+</route>
+<script setup lang="ts">
+import SectionHeading from '@/components/section-heading.vue'
+import { getDesignerInfo, updateDesignerInfo } from '../../../core/libs/requests'
+import { useUserStore } from '../../../store'
+import { storeToRefs } from 'pinia'
+import { pick } from 'radash'
+import BottomAppBar from '@/components/bottom-app-bar.vue'
+import { useMessage } from 'wot-design-uni'
+import { NetImages } from '../../../core/libs/net-images'
+
+const { alert } = useMessage()
+const userStore = useUserStore()
+const { userInfo } = storeToRefs(userStore)
+const form = ref<{
+  userId?: number
+  videoNumber?: string
+}>()
+const { data, run: setData } = useRequest(() => getDesignerInfo(userInfo.value.userId))
+const { loading, run: submiting } = useRequest(() => updateDesignerInfo(form.value))
+const posterUrl = ref()
+const handleSubmit = async () => {
+  await submiting()
+  await setData()
+  alert({ title: '关联成功', confirmButtonText: '我知道了' })
+}
+const createPoster = () => {
+  return new Promise((resolve, reject) => {
+    ;(async () => {
+      const { path } = await uni.getImageInfo({ src: data.value.homePageUrl })
+      // console.log(path)
+      // console.log(logoPath)
+      const { path: bgPath } = await uni.getImageInfo({ src: NetImages.InviteBg })
+      // console.log(bgPath)
+      const { path: logoPath } = await uni.getImageInfo({ src: NetImages.Logo })
+      const { path: avatarPath } = await uni.getImageInfo({
+        src: data.value.headImgUrl || userInfo.value?.avatar || NetImages.DefaultAvatar,
+      })
+      const [{ path: icon1 }, { path: icon2 }, { path: icon3 }, { path: icon4 }] =
+        await Promise.all([
+          uni.getImageInfo({
+            src: 'https://image.zhuchaohui.com/zhucaohui/315c5ca680bc6b47a5344dbff701cf1e6802d8a240754e25ef3d4308bc1deef6.png',
+          }),
+          uni.getImageInfo({
+            src: 'https://image.zhuchaohui.com/zhucaohui/ee83d30d553feb1482920e49c28824d73bb33838d996ec2f6c646ae6deab0330.png',
+          }),
+          uni.getImageInfo({
+            src: 'https://image.zhuchaohui.com/zhucaohui/9f89604f285c33200b7e6fda9acc82e130f34c2f5687cfeb78f3541e1fabcc28.png',
+          }),
+          uni.getImageInfo({
+            src: 'https://image.zhuchaohui.com/zhucaohui/2467be55426018856150bd94bbd21865df3d73ce982bc1c82b7585cf26c822f0.png',
+          }),
+        ])
+      const ctx = uni.createCanvasContext('firstCanvas')
+      const getPx = (width, designPx) => {
+        return (width / 319) * designPx
+      }
+      uni
+        .createSelectorQuery()
+        .select('#firstCanvas')
+        .fields({ size: true }, async ({ width: w, height: h }: any) => {
+          console.log(w, h)
+
+          ctx.drawImage(bgPath, 0, 0, w, h)
+          ctx.drawImage(path, 0, 0, w, w / 1.56)
+          ctx.drawImage(avatarPath, getPx(w, 17), getPx(w, 21), getPx(w, 28), getPx(w, 28))
+          ctx.drawImage(logoPath, getPx(w, 17), getPx(w, 230), getPx(w, 24), getPx(w, 24))
+          // 绘制昵称
+          ctx.setFillStyle('#ffffff')
+          ctx.setFontSize(getPx(w, 14))
+          ctx.fillText(userInfo.value?.nickname, getPx(w, 53), getPx(w, 40))
+          // 绘制文字
+          ctx.setFillStyle('#ffffff')
+          ctx.setFontSize(getPx(w, 18))
+          ctx.fillText('筑巢荟—助力设计师成长平台', getPx(w, 53), getPx(w, 248))
+
+          ctx.setFillStyle('#ffffff')
+          ctx.setFontSize(getPx(w, 12))
+          ctx.fillText('国内外设计游学', getPx(w, 62), getPx(w, 303))
+
+          ctx.setFillStyle('#ffffff')
+          ctx.setFontSize(getPx(w, 12))
+          ctx.fillText('设计赋能项目', getPx(w, 207), getPx(w, 303))
+
+          ctx.setFillStyle('#ffffff')
+          ctx.setFontSize(getPx(w, 12))
+          ctx.fillText('线上获客工具', getPx(w, 62), getPx(w, 339))
+
+          ctx.setFillStyle('#ffffff')
+          ctx.setFontSize(getPx(w, 12))
+          ctx.fillText('丰富线下活动', getPx(w, 207), getPx(w, 339))
+
+          ctx.drawImage(icon1, getPx(w, 41), getPx(w, 290), getPx(w, 18), getPx(w, 18))
+          ctx.drawImage(icon2, getPx(w, 189), getPx(w, 290), getPx(w, 18), getPx(w, 18))
+          ctx.drawImage(icon3, getPx(w, 41), getPx(w, 325), getPx(w, 18), getPx(w, 18))
+          ctx.drawImage(icon4, getPx(w, 189), getPx(w, 325), getPx(w, 18), getPx(w, 18))
+
+          ctx.draw(true, () => {
+            uni.canvasToTempFilePath({
+              canvasId: 'firstCanvas',
+              width: 300,
+              height: 460,
+              success: (res) => {
+                // console.log('生成海报', res)
+                resolve(res.tempFilePath)
+              },
+              fail: (err) => {
+                // uni.hideLoading()
+                reject(err)
+              },
+            })
+          })
+        })
+        .exec()
+    })()
+  })
+}
+const save = async () => {
+  await uni.saveImageToPhotosAlbum({ filePath: posterUrl.value })
+  uni.showToast({ title: '已保存相册', icon: 'none' })
+}
+const share = () => {
+  // uni.share({
+  //   provider: 'weixin',
+  //   scene: 'WXSceneSession',
+  //   type: 2,
+  //   // title: '你好呀',
+  //   // href: 'https://www.baidu.com/',
+  //   // summary: '我是图文描述',
+  //   imageUrl: posterUrl.value,
+  //   success: function (res) {
+  //     console.log('success:' + JSON.stringify(res))
+  //   },
+  //   fail: function (err) {
+  //     console.log('fail:' + JSON.stringify(err))
+  //   },
+  // })
+}
+onMounted(async () => {
+  await setData()
+  form.value = pick(data.value, ['id', 'userId', 'videoNumber'])
+  posterUrl.value = await createPoster()
+})
+// onAppShare(() => {})
+</script>
+<template>
+  <div
+    class="flex-grow flex flex-col justify-center gap-5 px-3.5 py-6 bg-black bg-[length:100%_100%]"
+    :style="{ backgroundImage: `url(${NetImages.InviteBg})` }"
+  >
+    <div class="block aspect-[0.69/1]">
+      <div class="bg-white mx-7 aspect-[0.69/1] relative rounded-2xl overflow-hidden">
+        <canvas class="w-full h-full" canvas-id="firstCanvas" id="firstCanvas"></canvas>
+        <div class="absolute bottom-5.5 left-5.5">
+          <cover-view>
+            <wd-button custom-class="bg-white/10!" @click="save">保存到相册</wd-button>
+          </cover-view>
+          <!-- <wd-button @click="share">微信</wd-button>
+        <wd-button @click="share">朋友圈</wd-button> -->
+        </div>
+      </div>
+    </div>
+    <!-- <SectionHeading title="如何关联视频号?" size="sm"></SectionHeading>
+    <img class="w-[347px] h-[186px] rounded-2xl" src="https://via.placeholder.com/347x186" />
+    <SectionHeading title="视频号ID" size="sm"></SectionHeading>
+    <div class="bg-[#f6f6f6] rounded-lg px-3.5 py-2.5">
+      <wd-input
+        v-model="form.videoNumber"
+        placeholder="请输入视频号ID"
+        no-border
+        custom-class="bg-[#f6f6f6]!"
+      ></wd-input>
+    </div>
+    <BottomAppBar fixed :border="false">
+      <div>
+        <div class="text-center mb-5.5">
+          <span class="text-black/40 text-xs font-normal font-['PingFang SC'] leading-tight">
+            点击确认关联即表示同意
+          </span>
+          <span class="text-[#0cbe7c] text-xs font-normal font-['PingFang SC'] leading-tight">
+            《个人微信视频号授权使用协议》
+          </span>
+        </div>
+        <wd-button :round="false" block :loading="loading" @click="handleSubmit">
+          确定关联
+        </wd-button>
+      </div>
+    </BottomAppBar> -->
+  </div>
+</template>

+ 13 - 6
packages/app/src/pages/mine/scan/result/index.vue

@@ -11,6 +11,8 @@ import { useRouter } from '../../../../core/utils/router'
 import { QrCodeBusinessType } from '../../../../core/libs/enums'
 import { success } from '../../../../core/libs/svgs'
 import SectionHeading from '@/components/section-heading.vue'
+import BottomAppBar from '@/components/bottom-app-bar.vue'
+import { NetImages } from '@/core/libs/net-images'
 
 const router = useRouter()
 const userStore = useUserStore()
@@ -71,32 +73,37 @@ onLoad(async (query: { result: string }) => {
 })
 </script>
 <template>
-  <div class="flex-grow flex flex-col items-center justify-center">
+  <div class="flex-grow flex flex-col items-center justify-center bg-white">
     <template v-if="isPointsCheckout">
-      <div class="flex items-center">
+      <div class="w-full flex items-center box-border py-5.5 px-3.5 gap-2.5">
         <wd-img
           class="rounded-full"
           width="45"
           height="45"
           custom-class="border border-[#f2f2f2] border-solid"
-          :src="data?.avatar"
+          :src="data?.avatar ? 'https://' + data?.avatar : NetImages.DefaultAvatar"
         />
         <div class="text-black/90 text-base font-normal font-['PingFang_SC'] leading-[10.18px]">
           {{ data?.name }}
         </div>
       </div>
-      <div>
+      <div class="flex items-end">
         <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="mt-8">
         <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>
+      <div class="flex-1"></div>
+      <BottomAppBar fixed safe-area-inset-bottom>
+        <div class="w-full">
+          <wd-button :round="false" block @click="handleSubmit">确认付款</wd-button>
+        </div>
+      </BottomAppBar>
     </template>
     <template v-else-if="isInStoreClockIn">
       <div class="w-full box-border px-8.25 flex flex-col items-center gap-7">

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

@@ -33,6 +33,7 @@ interface NavigateToOptions {
        "/pages/mine/convention/index" |
        "/pages/mine/coupons/index" |
        "/pages/mine/homepage/index" |
+       "/pages/mine/invite/index" |
        "/pages/mine/levels/index" |
        "/pages/mine/orders/index" |
        "/pages/mine/points/index" |