Bladeren bron

feat(mine): 优化代理个人页面并添加跟进记录功能

- 优化代理个人信息展示
- 添加跟进记录列表
- 修复邀请好友页面样式
- 优化 Canvas 绘图方法
EvilDragon 3 maanden geleden
bovenliggende
commit
6dc11bbfb6

+ 21 - 9
packages/app/src/core/utils/canvas.ts

@@ -131,15 +131,27 @@ export class Canvas {
   }
 
   /**
-   * 绘制水平的多个不同样式的文本 例如:'文本1: 123, 文本2: 456'
+   * 绘制水平居中一行多个不同样式的文本 例如:'文本1: 123, 文本2: 456'
    */
-  FillTexts(texts, colors, fontSizes, designX, designY, designSpacing) {
-    let x = designX
-    for (let i = 0; i < texts.length; i++) {
-      this.context.setFillStyle(colors[i])
-      this.context.setFontSize(this.px(fontSizes[i]))
-      this.context.fillText(texts[i], this.px(x), this.px(designY))
-      x += designSpacing[i]
-    }
+  FillTexts(textSegments: { text: string; font: string; color: string }[], designY): void {
+    // Calculate total text width
+    let totalWidth = 0
+    textSegments.forEach((segment) => {
+      this.context.font = segment.font
+      totalWidth += this.context.measureText(segment.text).width
+    })
+
+    // Starting x position for centered text
+    let startX = (this.size.width - totalWidth) / 2
+    const y = this.px(designY) // Fixed vertical position
+
+    // Draw each text segment
+    textSegments.forEach((segment) => {
+      this.context.font = segment.font
+      this.context.fillStyle = segment.color
+
+      this.context.fillText(segment.text, startX, y)
+      startX += this.context.measureText(segment.text).width // Move x position for next segment
+    })
   }
 }

+ 2 - 1
packages/merchant/src/pages.json

@@ -194,7 +194,8 @@
       "type": "page",
       "style": {
         "navigationBarTitleText": "邀请好友",
-        "navigationBarBackgroundColor": "#ffffff"
+        "navigationBarBackgroundColor": "#ffffff",
+        "navigationStyle": "custom"
       }
     },
     {

+ 1 - 1
packages/merchant/src/pages/agent/designer/detail.vue

@@ -41,7 +41,7 @@ const memberLevelsStore = useMemberLevelsStore()
 const { getMemberLevelLogo } = memberLevelsStore
 const id = ref()
 const { data, run: setData } = useRequest(() => getUserInfoById(id.value))
-const active = ref('product')
+const active = ref('integral')
 const tabs = ref([
   { label: '数据动态', value: 'integral' },
   { label: '跟进记录', value: 'followUp' },

+ 111 - 88
packages/merchant/src/pages/mine/agent/invite/index.vue

@@ -2,7 +2,8 @@
 {
   "style": {
     "navigationBarTitleText": "邀请好友",
-    "navigationBarBackgroundColor": "#ffffff"
+    "navigationBarBackgroundColor": "#ffffff",
+    "navigationStyle": "custom"
   }
 }
 </route>
@@ -11,6 +12,7 @@ import { getBroker } from '../../../../core/libs/requests'
 import { useUserStore } from '../../../../store/index'
 import { Canvas } from '@designer-hub/app/src/core/utils/canvas'
 import { storeToRefs } from 'pinia'
+import NavbarEvo from '@/components/navbar-evo.vue'
 
 const currentInstance = getCurrentInstance()
 const userStore = useUserStore()
@@ -21,95 +23,116 @@ const { data, run: setData } = useRequest(() =>
 
 onLoad(async () => {
   await setData()
-  const [bg, title, contentBg, icon1, icon2, icon3, icon4, icon5, avatar] = await Promise.all(
-    [
-      'https://image.zhuchaohui.com/zhucaohui/9352daa033edc343fd6cc983978857dd77386b573380871a4b7f9e9f3617f183.png',
-      'https://image.zhuchaohui.com/zhucaohui/f1f13041769ecdf462aa2ccc246fb7566bea6fe83aaf73717ee959629cfe0f8f.svg',
-
-      'https://image.zhuchaohui.com/zhucaohui/08abd942d9f5635aa3cfd60376706bc948cdaf556417fb3396204f7cdec42e30.png',
-
-      'https://image.zhuchaohui.com/zhucaohui/fc22d1bee4e1737dc0d592a065f277bec4a790496faa292c869f781e9c67a700.svg',
-      'https://image.zhuchaohui.com/zhucaohui/85594fe5a8b12dc815b3ea062e826de086ed204f840931980cd1bf5417d8b814.svg',
-      'https://image.zhuchaohui.com/zhucaohui/7c3e518ff2dcd2d4d39001e3970c492195d518c7f0015c86c00b9ac5e788e4f4.svg',
-      'https://image.zhuchaohui.com/zhucaohui/11b8448cc06352f796a49fbbc67e1eacfa7f382cb2507d066cd32b6f88f164fe.svg',
-      'https://image.zhuchaohui.com/zhucaohui/d9dc32881076a752a7ab72701321538ff0e10fbc0482a3b4f9e95c1b8a132aec.svg',
-      data.value.headImgUrl || '',
-    ].map((it) => uni.getImageInfo({ src: it }).then(({ path }) => path)),
-  )
-
-  const ctx = uni.createCanvasContext('qrcode', currentInstance)
-  const { width, height } = await new Promise<any>((resolve) => {
-    uni
-      .createSelectorQuery()
-      .in(currentInstance)
-      .select('#qrcode')
-      .fields({ node: true, size: true }, (res) => {
-        resolve(res)
-      })
-      .exec()
-  })
-  const canvas = new Canvas(ctx, { width, height }, { width: 351 })
-  canvas.FillImage(bg)
-  canvas.RoundRect('#5379EC', 0, 0, 352, 607, 16)
-  canvas.Image(title, 27, 31, 297, 22.3)
-
-  canvas.Image(icon1, 28, 69, 15, 15)
-  canvas.Image(icon2, 143, 69, 13, 13)
-  canvas.Image(icon3, 248, 69, 13, 13)
-  canvas.Image(icon4, 30, 100, 12, 12)
-  canvas.Image(icon5, 141, 96, 16, 16)
-
-  canvas.FillText('国内外设计游学', '#ffffff', 10, 46, 80)
-  canvas.FillText('线上获客工具', '#ffffff', 10, 159, 80)
-  canvas.FillText('设计赋能项目', '#ffffff', 10, 262, 80)
-  canvas.FillText('丰富线下活动', '#ffffff', 10, 46, 109)
-  canvas.FillText('周边商品好礼', '#ffffff', 10, 159, 109)
-
-  // canvas.Circle('#D9D9D9', 136, 129, 41)
-
-  canvas.Image(contentBg, 17, 128, 319, 461)
-  canvas.CircleImage(avatar, 140, 133, 37)
-
-  canvas.FillText(data.value.brokerName, '#000000D9', 18, 149, 228, 'center')
-
-  canvas.FillTexts(
-    ['邀请码:', data.value.inviteCode],
-    ['14px PingFang_SC', '18px PingFang_SC'],
-    ['rgba(0,0,0,0.65)', 'rgba(0,0,0,0.85)'],
-    0,
-    0,
-    '',
-  )
-  // const textSegments = [
-  //   { text: '邀请码:', font: '14px PingFang_SC', color: 'rgba(0,0,0,0.65)' },
-  //   { text: data.value.inviteCode || '', font: '18px PingFang_SC', color: 'rgba(0,0,0,0.85)' },
-  // ]
-
-  // // Calculate total text width
-  // let totalWidth = 0
-  // textSegments.forEach((segment) => {
-  //   ctx.font = segment.font
-  //   totalWidth += ctx.measureText(segment.text).width
-  // })
-
-  // // Starting x position for centered text
-  // let startX = (width - totalWidth) / 2
-  // const y = 246 // Fixed vertical position
-
-  // // Draw each text segment
-  // textSegments.forEach((segment) => {
-  //   ctx.font = segment.font
-  //   ctx.fillStyle = segment.color
-
-  //   ctx.fillText(segment.text, startX, y)
-  //   startX += ctx.measureText(segment.text).width // Move x position for next segment
-  // })
-
-  ctx.draw && ctx.draw()
+  try {
+    const [bg, title, contentBg, icon1, icon2, icon3, icon4, icon5, avatar] = await Promise.all(
+      [
+        'https://image.zhuchaohui.com/zhucaohui/9352daa033edc343fd6cc983978857dd77386b573380871a4b7f9e9f3617f183.png',
+        'https://image.zhuchaohui.com/zhucaohui/f1f13041769ecdf462aa2ccc246fb7566bea6fe83aaf73717ee959629cfe0f8f.svg',
+
+        'https://image.zhuchaohui.com/zhucaohui/08abd942d9f5635aa3cfd60376706bc948cdaf556417fb3396204f7cdec42e30.png',
+
+        'https://image.zhuchaohui.com/zhucaohui/fc22d1bee4e1737dc0d592a065f277bec4a790496faa292c869f781e9c67a700.svg',
+        'https://image.zhuchaohui.com/zhucaohui/85594fe5a8b12dc815b3ea062e826de086ed204f840931980cd1bf5417d8b814.svg',
+        'https://image.zhuchaohui.com/zhucaohui/7c3e518ff2dcd2d4d39001e3970c492195d518c7f0015c86c00b9ac5e788e4f4.svg',
+        'https://image.zhuchaohui.com/zhucaohui/11b8448cc06352f796a49fbbc67e1eacfa7f382cb2507d066cd32b6f88f164fe.svg',
+        'https://image.zhuchaohui.com/zhucaohui/d9dc32881076a752a7ab72701321538ff0e10fbc0482a3b4f9e95c1b8a132aec.svg',
+        data.value.headImgUrl || '',
+      ].map((it) =>
+        uni
+          .getImageInfo({ src: it })
+          .then(({ path }) => path)
+          .catch(() =>
+            uni
+              .getImageInfo({
+                src: 'https://image.zhuchaohui.com/zhucaohui/6f858f282ce597abd2dd2717264b0cdde1566c4f8d5f3baa20fb7cf4c2e11a2f.png',
+              })
+              .then(({ path }) => path),
+          ),
+      ),
+    )
+    const ctx = uni.createCanvasContext('qrcode', currentInstance)
+    const { width, height } = await new Promise<any>((resolve) => {
+      uni
+        .createSelectorQuery()
+        .in(currentInstance)
+        .select('#qrcode')
+        .fields({ node: true, size: true }, (res) => {
+          resolve(res)
+        })
+        .exec()
+    })
+    const canvas = new Canvas(ctx, { width, height }, { width: 351 })
+    canvas.FillImage(bg)
+    canvas.RoundRect('#5379EC', 0, 0, 352, 607, 16)
+    canvas.Image(title, 27, 31, 297, 22.3)
+
+    canvas.Image(icon1, 28, 69, 15, 15)
+    canvas.Image(icon2, 143, 69, 13, 13)
+    canvas.Image(icon3, 248, 69, 13, 13)
+    canvas.Image(icon4, 30, 100, 12, 12)
+    canvas.Image(icon5, 141, 96, 16, 16)
+
+    canvas.FillText('国内外设计游学', '#ffffff', 10, 46, 80)
+    canvas.FillText('线上获客工具', '#ffffff', 10, 159, 80)
+    canvas.FillText('设计赋能项目', '#ffffff', 10, 262, 80)
+    canvas.FillText('丰富线下活动', '#ffffff', 10, 46, 109)
+    canvas.FillText('周边商品好礼', '#ffffff', 10, 159, 109)
+
+    // canvas.Circle('#D9D9D9', 136, 129, 41)
+
+    canvas.Image(contentBg, 17, 128, 319, 461)
+    canvas.CircleImage(avatar, 140, 133, 37)
+
+    // canvas.FillText(data.value.brokerName, '#000000D9', 18, 149, 228, 'center')
+    canvas.FillTexts(
+      [{ text: data.value?.brokerName ?? '', font: '18px PingFang SC', color: '#000000D9' }],
+      228,
+    )
+
+    const textSegments = [
+      { text: '邀请码:', font: '14px PingFang_SC', color: 'rgba(0,0,0,0.65)' },
+      { text: data.value.inviteCode || '', font: '18px PingFang_SC', color: 'rgba(0,0,0,0.85)' },
+    ]
+    canvas.FillTexts(textSegments, 240)
+
+    // // Calculate total text width
+    // let totalWidth = 0
+    // textSegments.forEach((segment) => {
+    //   ctx.font = segment.font
+    //   totalWidth += ctx.measureText(segment.text).width
+    // })
+
+    // // Starting x position for centered text
+    // let startX = (width - totalWidth) / 2
+    // const y = 246 // Fixed vertical position
+
+    // // Draw each text segment
+    // textSegments.forEach((segment) => {
+    //   ctx.font = segment.font
+    //   ctx.fillStyle = segment.color
+
+    //   ctx.fillText(segment.text, startX, y)
+    //   startX += ctx.measureText(segment.text).width // Move x position for next segment
+    // })
+
+    ctx.draw && ctx.draw()
+  } catch (e) {
+    console.log(e)
+  }
 })
 </script>
 <template>
-  <div class="aspect-[0.58/1]">
-    <canvas class="w-full h-full" id="qrcode" canvas-id="qrcode"></canvas>
+  <!--  bg-[url(https://image.zhuchaohui.com/zhucaohui/a7694a131a9119b055cff320aa9c363ad2012e77229038a011110e64d1cf4da8.png)-->
+  <div class="flex-grow bg-[length:100%_100%] bg-[#5379ec]! ]">
+    <NavbarEvo transparent dark placeholder></NavbarEvo>
+    <div class="aspect-[0.58/1]">
+      <canvas class="w-full h-full" id="qrcode" canvas-id="qrcode"></canvas>
+    </div>
+    <div class="px-6 flex justify-between">
+      <div class="flex-1">
+        <wd-button plain custom-class="bg-[#4B6DEA]! border-white! text-white!">保存图片</wd-button>
+      </div>
+      <!--     <div> <wd-button custom-class="bg-white!"></wd-button></div>-->
+    </div>
   </div>
 </template>

+ 81 - 9
packages/merchant/src/pages/mine/components/agent-mine.vue

@@ -11,6 +11,10 @@ import { storeToRefs } from 'pinia'
 import qrCode from '@designer-hub/assets/src/libs/assets/qrCode'
 import Card from '@designer-hub/app/src/components/card.vue'
 import SectionHeading from '@designer-hub/app/src/components/section-heading.vue'
+import { getFollowUpPage } from '@/core/libs/agent-requests'
+import { locationIcon } from '@designer-hub/assets/src/svgs'
+import { dayjs } from 'wot-design-uni'
+import PageHelperEvo from '@/components/page-helper-evo.vue'
 
 const userStore = useUserStore()
 const { userInfo } = storeToRefs(userStore)
@@ -20,6 +24,12 @@ const { data: agent, run: setAgent } = useRequest(() =>
 const { data: yearTarget, run: setYearTarget } = useRequest(() => getYearTarget())
 const { data: designerData, run: setdesignerData } = useRequest(() => getDesignerStatistics())
 const { data: followData, run: setFollowData } = useRequest(() => getFollowStatistics())
+const bgClass = [
+  'bg-gradient-to-r from-[#fef3ee] to-[#f0f4f9]',
+  'bg-gradient-to-r from-[#fef8ee] to-[#f0f4f9]',
+  'bg-gradient-to-r from-[#eef4fe] to-[#f0f4f9]',
+  'bg-gradient-to-r from-[#faf2ff] to-[#f0f4f9]',
+]
 const toSettings = () => {
   uni.navigateTo({ url: '/pages/mine/agent/settings/index' })
 }
@@ -56,14 +66,14 @@ onMounted(async () => {
           width="56"
           height="56"
           custom-class="border border-solid border-white"
-          :src="agent.headImgUrl"
+          :src="agent?.headImgUrl"
         />
         <div class="mx-4 flex-1">
           <div class="text-white text-lg font-normal font-['PingFang_SC'] leading-normal">
-            {{ agent.brokerName }}
+            {{ agent?.brokerName }}
           </div>
           <div class="text-white text-xs font-normal font-['PingFang_SC'] leading-relaxed">
-            ID:{{ agent.inviteCode }}
+            ID:{{ agent?.inviteCode }}
           </div>
         </div>
         <div class="flex flex-col items-center" @click.stop="toInvite">
@@ -79,9 +89,7 @@ onMounted(async () => {
         <SectionHeading title="本年目标" size="base"></SectionHeading>
         <div class="flex flex-col gap-2.5 mt-3">
           <template v-for="(it, i) in yearTarget" :key="i">
-            <div
-              class="bg-gradient-to-r from-[#fef3ee] to-[#f0f4f9] rounded-lg flex items-center p-4 gap-6"
-            >
+            <div class="rounded-lg flex items-center p-4 gap-6" :class="bgClass[i]">
               <div>
                 <div class="w-[45px] h-[45px] rounded-full border-4 border-[#ffe2d0]">
                   <div style="width: 50px; height: 50px">
@@ -130,7 +138,7 @@ onMounted(async () => {
                   <div class="text-black/60 text-xs font-normal font-['PingFang_SC']">差值</div>
                   <div class="text-[#ff2d2d] text-xs font-medium font-['DIN'] leading-normal">
                     <!-- 3000 -->
-                    {{ (it.target - it.thisYearComplete) / 10000 }}
+                    {{ (it.target - (it.thisYearComplete ?? 0)) / 10000 }}
                   </div>
                   <div class="text-[#ff2d2d] text-[10px] font-medium font-['DIN'] leading-normal">
@@ -142,7 +150,7 @@ onMounted(async () => {
                 <div class="flex items-center gap-1">
                   <div class="text-black/90 text-lg font-medium font-['DIN'] leading-normal">
                     <!-- 6000 -->
-                    {{ it.thisYearComplete / 10000 }}
+                    {{ (it.thisYearComplete ?? 0) / 10000 }}
                   </div>
                   <div class="text-black text-xs font-normal font-['PingFang_SC']">万</div>
                 </div>
@@ -150,7 +158,7 @@ onMounted(async () => {
                   <div class="text-black/60 text-xs font-normal font-['PingFang_SC']">本月</div>
                   <div class="text-[#0FC187] text-xs font-medium font-['DIN'] leading-normal">
                     <!-- 3000 -->
-                    {{ it.thisMonthComplete / 10000 }}
+                    {{ (it.thisMonthComplete ?? 0) / 10000 }}
                   </div>
                   <div class="text-[#0FC187] text-[10px] font-medium font-['DIN'] leading-normal">
@@ -207,6 +215,70 @@ onMounted(async () => {
           </template>
         </div>
       </Card>
+      <PageHelperEvo :request="getFollowUpPage" :query="{ brokerId: userInfo.userId }">
+        <template #default="{ source }">
+          <div class="flex flex-col gap-4">
+            <template v-for="(it, index) in source?.list" :key="index">
+              <div class="bg-white rounded-2xl shadow pl-[15px] py-[15px] flex-col gap-2 flex">
+                <div class="flex items-center justify-between">
+                  <div
+                    class="text-black/90 text-base font-normal font-['PingFang_SC'] leading-relaxed"
+                  >
+                    {{ dayjs(it.followTime).format('YYYY-MM-DD HH:mm') }}
+                  </div>
+                  <div
+                    class="text-white text-xs font-normal font-['PingFang_SC'] leading-none pa-[8px]"
+                    :class="`${{ 1: 'bg-[#2357E9]', 2: 'bg-[#f8b344]' }[it.followType]}`"
+                    style="border-top-left-radius: 15px; border-bottom-left-radius: 5px"
+                  >
+                    <div class="flex items-center gap-1">
+                      <div class="w-1 h-1 bg-white rounded-full border"></div>
+                      <!-- 线下拜访 -->
+                      {{ it.followTypeName }}
+                    </div>
+                  </div>
+                </div>
+                <div
+                  class="text-black/60 text-sm font-normal font-['PingFang_SC'] leading-normal mr-[15px] mt-[29px]"
+                >
+                  <!-- 和周老师在工作碰了环球项目,选了瓷砖款式,后天客户交定金,订单金额初步为
+                  304958 -->
+                  {{ it.remark }}
+                </div>
+                <div class="mt-[15px] flex gap-2.5">
+                  <template v-for="(src, index) in it?.imgUrl?.split(',')" :key="index">
+                    <wd-img
+                      custom-class="rounded-lg overflow-hidden"
+                      width="70"
+                      height="70"
+                      :src="src"
+                    />
+                  </template>
+                </div>
+                <div class="flex items-center justify-between mt-[19px]">
+                  <div
+                    class="h-[25px] px-1.5 bg-[#f4f4f4] rounded-md justify-center items-center gap-1 inline-flex"
+                  >
+                    <wd-img width="15px" height="15px" :src="locationIcon"></wd-img>
+
+                    <div
+                      class="text-black/40 text-[10px] font-normal font-['PingFang_SC'] leading-[25px]"
+                    >
+                      <!-- 一间空间设计工作室 -->
+                      {{ it?.address.address }}
+                    </div>
+                  </div>
+                  <div
+                    class="text-black/90 text-sm font-normal font-['PingFang_SC'] leading-relaxed mr-[15px]"
+                  >
+                    渠道:{{ it?.brokerName }}
+                  </div>
+                </div>
+              </div>
+            </template>
+          </div>
+        </template>
+      </PageHelperEvo>
     </div>
   </div>
 </template>