Browse Source

Merge remote-tracking branch 'origin/main' into main

赵添更 6 days ago
parent
commit
3d6695e04f
26 changed files with 442 additions and 151 deletions
  1. BIN
      packages/app/src/assets/pngs/calculator-bg.png
  2. 5 3
      packages/app/src/components/moment-item.vue
  3. 21 0
      packages/app/src/core/libs/models.ts
  4. 21 3
      packages/app/src/pages-sub/home/activity/images/index.vue
  5. 1 1
      packages/app/src/pages-sub/home/components/activity-as-of.vue
  6. 2 2
      packages/app/src/pages-sub/home/components/activity-count-down.vue
  7. 22 10
      packages/app/src/pages-sub/home/content/index.vue
  8. 45 40
      packages/app/src/pages-sub/home/mall/confirm-order/index.vue
  9. 15 2
      packages/app/src/pages-sub/home/spread/product-detail/index.vue
  10. 1 0
      packages/app/src/pages-sub/mine/homepage/edit/index.vue
  11. 1 1
      packages/app/src/pages-sub/mine/honors/detail/index.vue
  12. 1 1
      packages/app/src/pages-sub/mine/honors/index.vue
  13. 1 1
      packages/app/src/pages/home/components/activity-count-down.vue
  14. 2 2
      packages/app/src/pages/home/components/home-banner.vue
  15. 5 1
      packages/app/src/utils/http.ts
  16. BIN
      packages/merchant/src/assets/pngs/email.png
  17. BIN
      packages/merchant/src/assets/pngs/local.png
  18. BIN
      packages/merchant/src/assets/pngs/phone.png
  19. BIN
      packages/merchant/src/assets/pngs/wechat.png
  20. 9 1
      packages/merchant/src/core/libs/agent-requests.ts
  21. 8 0
      packages/merchant/src/core/libs/pngs.ts
  22. 15 0
      packages/merchant/src/core/libs/requests.ts
  23. 65 2
      packages/merchant/src/pages/agent/designer/detail.vue
  24. 107 62
      packages/merchant/src/pages/mine/agent/business-card/edit-card.vue
  25. 92 16
      packages/merchant/src/pages/mine/agent/business-card/index.vue
  26. 3 3
      packages/merchant/src/pages/mine/components/agent-mine.vue

BIN
packages/app/src/assets/pngs/calculator-bg.png


+ 5 - 3
packages/app/src/components/moment-item.vue

@@ -39,13 +39,15 @@ const isVideo = computed(
   () => props.options.bannerUrls?.length && isImageOrVideo(props.options.bannerUrls[0]) === 'video',
 )
 const toDetail = () => {
-	if(props.options.detailsType==='1'){
+	console.log(props.options)
+	if(props.options.detailsType==='2'){
 		uni.navigateTo({
-		  url: `/pages-sub/home/moment/index?${stringify({ id: props.options.id })}${props.isShared ? '&isShared=true' : ''}`,
+		  url: props.options.detailsUrl,
 		})
+		
 	}else{
 		uni.navigateTo({
-		  url: props.options.detailsUrl,
+		  url: `/pages-sub/home/moment/index?${stringify({ id: props.options.id })}${props.isShared ? '&isShared=true' : ''}`,
 		})
 	}  
 }

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

@@ -1283,6 +1283,27 @@ export interface ReferralRes {
 
 }
 
+export interface FollowUpRes {
+  /* 线下拜访 */
+  offline: number
+
+  /* 今年线下拜访 */
+  thisYearOffline: number
+
+  /* 线上跟进 */
+  online: number
+
+  /* 今年线上跟进 */
+  thisYearOnline: number
+  
+  /* 累计跟进次数 */
+  totality: number
+  
+  /* 今年累计跟进次数 */
+  thisYearTotality: number
+
+}
+
 export enum DictType {
   /**
    *  擅长空间类型

+ 21 - 3
packages/app/src/pages-sub/home/activity/images/index.vue

@@ -46,9 +46,15 @@ const previewImg = (i,index) => {
 	})
 }
 const previewImgNew = (i) => {
-	console.log(data)
+	let imgArr = [];
+	for(let i in imgList.value){
+		if(imgList.value[i].type==='2'){
+			imgArr.push(imgList.value[i].url)
+		}
+	}
 	uni.previewImage({
-		urls:[i]
+		urls:imgArr,
+		current:i
 	})
 }
 const timeTabChange = async (it,i) =>{
@@ -91,6 +97,12 @@ onLoad(async (query: { id: string; type: 'activity' | 'studyTour'; title: string
 	  }
   }
 })
+onShareAppMessage(()=>{
+	
+})
+onShareTimeline(()=>{
+	
+})
 </script>
 <template>
   <div class="bg-white gap-4 pb-4">
@@ -103,7 +115,13 @@ onLoad(async (query: { id: string; type: 'activity' | 'studyTour'; title: string
 		    :title="'第' + Number(i + 1) + '天 ' + dayjs(img.travelDate).format('YYYY-MM-DD')"
 		  ></SectionHeading> -->
 		  <video v-if="it.type=='1'" class="w-full" :src="it.url"></video>
-		  <wd-img v-else width="100%" mode="widthFix" :src="it.url" @click="previewImgNew(it.url)" />
+		  <wd-img v-else width="100%" mode="widthFix" :src="it.url" @click="previewImgNew(i)">
+			  <template #loading>
+				<view class="loading-wrap">
+				  <wd-loading />
+				</view>
+			  </template>
+		  </wd-img>
 		  <!-- <template v-for="(video, index) in it.video" :key="index">
 		    <video class="w-full" :src="video"></video>
 		  </template>

+ 1 - 1
packages/app/src/pages-sub/home/components/activity-as-of.vue

@@ -34,7 +34,7 @@ onUnmounted(() => {
 </script>
 <template>
   <div>
-    <wd-count-down :time="time" @finish="emits('end')">
+    <wd-count-down v-if="status!=='running'" :time="time" @finish="emits('end')">
       <template #default="{ current }">
         <div
           class="flex items-center text-white text-xs font-normal font-['PingFang_SC'] leading-normal whitespace-nowrap"

+ 2 - 2
packages/app/src/pages-sub/home/components/activity-count-down.vue

@@ -26,10 +26,10 @@ const time = ref(
     <wd-count-down :time="time" @finish="emits('end')">
       <template #default="{ current }">
         <div v-if="time" class="flex items-center gap-1.25 text-black/40 text-sm">
-          <div>距{{ { waiting: '报名开始', running: '报名结束' }[status] }}还有</div>
+          <div class="text-[23rpx]">距{{ { waiting: '报名开始', running: '报名结束' }[status] }}还有</div>
           <div
             v-if="current.days"
-            class="w-4 h-4 bg-black/90 rounded text-white text-2.5 flex items-center justify-center"
+            class="w-5 h-4 bg-black/90 rounded text-white text-2.5 flex items-center justify-center"
           >
             {{ current.days }}
           </div>

+ 22 - 10
packages/app/src/pages-sub/home/content/index.vue

@@ -8,7 +8,7 @@
 </route>
 <script setup lang="ts">
 import { logo } from '../../../core/libs/svgs'
-import { getBanner, getContent, getHomeBanner } from '../../../core/libs/requests'
+import { getBanner, getContent, getHomeBanner, updateSetIndexConfig } from '../../../core/libs/requests'
 import Article from '../components/article.vue'
 import { Content } from '../../../core/libs/models'
 
@@ -67,6 +67,7 @@ const formatData = (res) => {
     data: {
       isBanner: true,
       title: res.name,
+	  coverVideoImage: res.coverVideoImage,
       contentDetail: res.bannerDetailsContent,
     },
   }
@@ -74,6 +75,7 @@ const formatData = (res) => {
 onLoad(async (query: { id: string; type?: 'banner'; data?: string }) => {
   id.value = query.id
   type.value = query.type
+  await updateSetIndexConfig({id:query.id})
   if (type.value === 'banner') {
     request.value = () =>
       getBanner(id.value).then((res) => {
@@ -86,18 +88,13 @@ onLoad(async (query: { id: string; type?: 'banner'; data?: string }) => {
             isBanner: true,
             title: res.data?.name,
             contentDetail: res.data?.bannerDetailsContent,
+			coverVideoImage:res.data?.coverVideoImage,
             createTime: res.data?.createTime.toString(),
             viewsCount: res.data?.viewCount,
           },
         }
       })
   } else if (type.value === 'home-banner') {
-    // console.log('home-banner', query.data)
- //    bannerHome.value = JSON.parse(decodeURIComponent(query.data))
-	// console.log('home-banner', bannerHome.value)
- //    uni.setNavigationBarTitle({
- //      title: bannerHome.value.name,
- //    })
 	request.value = () => getHomeBanner(id.value).then((res) => {
         uni.setNavigationBarTitle({
           title: res.data?.name,
@@ -108,6 +105,7 @@ onLoad(async (query: { id: string; type?: 'banner'; data?: string }) => {
             isBanner: true,
             title: res.data?.name,
             contentDetail: res.data?.details,
+			coverVideoImage:res.data?.coverVideoImage,
             createTime: res.data?.createTime.toString()
           },
         }
@@ -118,11 +116,18 @@ onLoad(async (query: { id: string; type?: 'banner'; data?: string }) => {
   await run()
 })
 onShareAppMessage(() => {
+	console.log(data.value)
 	if(type.value === 'home-banner'){
 		return {
-		  title: bannerHome.value.name,
-		  path:"/pages-sub/home/content/index?type=home-banner&id=" + id.value +"&data=" + encodeURIComponent(JSON.stringify(bannerHome.value)),
-		  imageUrl:bannerHome.value.coverVideoImage
+		  title: data.value.title? data.value.title:bannerHome.value.name,
+		  path:"/pages-sub/home/content/index?type=home-banner&id=" + id.value +"&data=" + encodeURIComponent(JSON.stringify(data.value?data.value:bannerHome.value)),
+		  imageUrl:data.value.coverVideoImage?data.value.coverVideoImage:bannerHome.value.coverVideoImage
+		}
+	}else if(type.value === 'banner'){		
+		return {
+		  title: data.value.title? data.value.title:bannerHome.value.name,
+		  path:"/pages-sub/home/content/index?type=banner&id=" + id.value +"&data=" + encodeURIComponent(JSON.stringify(data.value?data.value:bannerHome.value)),
+		  imageUrl:data.value.coverVideoImage?data.value.coverVideoImage:bannerHome.value.coverVideoImage
 		}
 	}else{
 		return {
@@ -131,12 +136,19 @@ onShareAppMessage(() => {
 	}
 })
 onShareTimeline(() =>{
+	console.log(bannerHome.value)
 	if(type.value === 'home-banner'){
 		return {
 		  title: bannerHome.value.name,
 		  path:"/pages-sub/home/content/index?type=home-banner&id=" + id.value +"&data=" + encodeURIComponent(JSON.stringify(bannerHome.value)),
 		  imageUrl:bannerHome.value.coverVideoImage
 		}
+	}else if(type.value === 'banner'){
+		return {
+		  title: bannerHome.value.name,
+		  path:"/pages-sub/home/content/index?type=banner&id=" + id.value +"&data=" + encodeURIComponent(JSON.stringify(bannerHome.value)),
+		  imageUrl:bannerHome.value.coverVideoImage
+		}
 	}else{
 		return {
 			title: data.value?.title,

+ 45 - 40
packages/app/src/pages-sub/home/mall/confirm-order/index.vue

@@ -22,14 +22,14 @@ import CouponsSelector from '@/pages-sub/common/components/coupons-selector.vue'
 import { right } from '../../../../core/libs/svgs'
 import ButtonEvo from '@/components/button-evo.vue'
 import { handleClickInstruction } from '../../../../core/libs/actions'
-import { useMessage } from 'wot-design-uni'
 
 const router = useRouter()
 const userStore = useUserStore()
 const { userInfo } = storeToRefs(userStore)
 const show = ref(false)
+const showUse = ref(false)
 const disabled = ref(false)
-const { alert } = useMessage()
+
 const data = ref()
 const selectedCoupons = ref<Coupon[]>([])
 const requestData = computed(() => ({
@@ -71,43 +71,12 @@ const offerPoints = computed(() => {
   return Number(sumBrandPoints)
 })
 const handlePay = async () => {
-  console.log(111)
-  if(disabled.value){
-	  return false
-  }
-  disabled.value = true;
+	disabled.value = false
+	if(disabled.value){
+		return false
+	}
   if(coupons.value.length>0 && selectedCoupons.value?.length===0){
-	  uni.showModal({
-	  	title:"提示",
-		content:"您有" + coupons.value.length + "张该商品的免费兑换券,使用后可免费兑换该商品,是否需要使用?",
-		cancelText:"不使用",
-		confirmText:"使用",
-		success: async (res) => {
-			if(res.confirm){
-				handleQ()
-			}
-			if(res.cancel){
-				const couponList =
-				  selectedCoupons.value?.map((it) => ({
-				    couponId: it.id,
-				    projectIds: it.productIds,
-				    buinessId: it.buinessId,
-				  })) || []
-				const { code } = await requestToast(
-				  () =>
-				    orderPay({
-				      ...data?.value,
-				      couponList,
-				    }),
-				  { success: true, successTitle: '兑换成功' },
-				)
-				disabled.value = false;
-				if (code === 0) {
-				  await router.replace('/pages-sub/home/mall/purchased/success/index')
-				}
-			}
-		}
-	  })
+	  showUse.value = true;	  
   }else{
 	  const couponList =
 	    selectedCoupons.value?.map((it) => ({
@@ -126,14 +95,40 @@ const handlePay = async () => {
 	  if (code === 0) {
 	    await router.replace('/pages-sub/home/mall/purchased/success/index')
 	  }
-	  disabled.value = false;
   }
   
 }
+const overlayCancel = () =>{
+	disabled.value = false
+	showUse.value = false;
+}
+const handleNoUse = async() => {
+	const couponList =
+	  selectedCoupons.value?.map((it) => ({
+	    couponId: it.id,
+	    projectIds: it.productIds,
+	    buinessId: it.buinessId,
+	  })) || []
+	const { code } = await requestToast(
+	  () =>
+	    orderPay({
+	      ...data?.value,
+	      couponList,
+	    }),
+	  { success: true, successTitle: '兑换成功' },
+	)
+	if (code === 0) {
+	  await router.replace('/pages-sub/home/mall/purchased/success/index')
+	}
+}
+const handleUser = async() => {
+	showUse.value = false;
+	disabled.value = false
+	handleQ()	
+}
 const handleQ = async () => {
   // await setCoupons()
   show.value = true
-  disabled.value = false;
 }
 const handleSelect = (coupon: Coupon) => {
   selectedCoupons.value = [coupon]
@@ -254,6 +249,16 @@ onLoad(async (query: { data: string }) => {
       @click-instruction="(e) => handleClickInstruction(alert, e)"
     ></CouponsSelector>
     <!-- <CouponsSelector></CouponsSelector> -->
+	<wd-overlay :show="showUse" @click="overlayCancel">
+	  <view class="bg-white w-[600rpx] fixed top-[50%] left-[50%] translate-[-50%,-50%] rounded-[20rpx] px-[20rpx] py-[30rpx]">
+	    <view class="text-center text-[36rpx] font-bold leading-[60rpx] mb-[40rpx]">提示</view>
+		<view class="text-black/60 px-[40rpx]">您有{{coupons.length}}张该商品的免费兑换券,使用后可免费兑换该商品,是否需要使用?</view>
+		<view class="flex pt-[40rpx]">
+			<wd-button type="info" @click="handleNoUse">不使用</wd-button>
+			<wd-button @click="handleUser">使用</wd-button>
+		</view>
+	  </view>
+	</wd-overlay>
     <!-- <wd-action-sheet title="优惠券" v-model="show">
       <view class="">
         <wd-tabs>

+ 15 - 2
packages/app/src/pages-sub/home/spread/product-detail/index.vue

@@ -27,6 +27,7 @@ const { clickByPermission } = usePermissions()
 const id = ref()
 const show = ref(false)
 const a = ref(1)
+const nums = ref(1)
 const item = ref()
 const type = ref<'add2Cart' | 'orderNow'>()
 const { data, run: setData } = useRequest(() => getProduct(id.value))
@@ -41,7 +42,7 @@ const handleConfirm = async () => {
         {
           productId: id.value,
           points: data.value.showFavourable ? data.value.favourablePoints : data.value.points,
-          nums: 1,
+          nums: nums.value,
           productName: data.value.prodcutName,
           orderImgUrl: data.value.productCoverImgUrl,
           vendorId: data.value.vendorId,
@@ -154,7 +155,7 @@ onShareTimeline(() => ({
         </swiper>
       </div>
     </div>
-    <div class="relative flex-1 bg-white py-7 px-[20rpx] flex flex-col gap-6 rounded-tl-2xl rounded-tr-2xl">
+    <div class=" overflow-hidden relative flex-1 bg-white py-7 px-[20rpx] flex flex-col gap-6 rounded-tl-2xl rounded-tr-2xl">
       <div
         v-if="String(data?.needPoints) === '1'"
         class="text-black/60 text-base font-normal font-['PingFang_SC'] leading-4"
@@ -236,6 +237,18 @@ onShareTimeline(() => ({
                 积分
               </div>
               <div class="flex-1"></div>
+			  <wd-input-number
+			    v-model="nums"
+			    :max="
+			      data?.isRestrict === 1 && data?.purchaseLimit
+			        ? Math.min(data?.purchaseQuantity, data?.productRepertory)
+			        : data?.isRestrict === 1 && !data?.purchaseLimit
+			          ? data?.productRepertory
+			          : data?.isRestrict === 2 && data?.purchaseLimit
+			            ? data?.purchaseQuantity
+			            : 9999
+			    "
+			  />
             </div>
           </div>
         </div>

+ 1 - 0
packages/app/src/pages-sub/mine/homepage/edit/index.vue

@@ -141,6 +141,7 @@ onMounted(async () => {
           <SectionHeading title="服务客户数">
             <template #start>
               <wd-input
+			    type="number"
                 placeholder="请输入真实客户数"
                 no-border
                 v-model="form.serviceCustomerCount"

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

@@ -66,7 +66,7 @@ const createPoster = () => {
           canvas.FillImage(bgPath)
           // ctx.drawImage(path, 0, 0, 16, 18)
           canvas.CircleImage(avatarPath, 17, 20, 20)
-          canvas.Image(badgePath, 93, 74, 170, 180)
+          canvas.Image(badgePath, 93, 74, 180, 180)
           canvas.FillText(userInfo.value?.nickname, '#ffffff', 16, 63, 46)
           const textNameWidth = ctx.measureText(data.value.name).width
           const textDesWidth = ctx.measureText(subTitle.value).width

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

@@ -274,7 +274,7 @@ onLoad(async (query?: Record<string | 'active' | 'id' | 'isShared', string>) =>
           <div class="grid grid-cols-3 gap-y-6">
             <template v-for="(item, i) in it" :key="i">
               <div
-                class="w-full px-4 box-border"
+                class="w-full px-1 box-border"
                 @click="
                   router.push(
                     `/pages-sub/mine/honors/detail/index?type=badge&id=${id}&data=${JSON.stringify(item)}`,

+ 1 - 1
packages/app/src/pages/home/components/activity-count-down.vue

@@ -22,7 +22,7 @@ const time = ref(
 )
 </script>
 <template>
-  <div>
+  <div v-if="status!=='running'">
     <wd-count-down :time="time" @finish="emits('end')">
       <template #default="{ current }">
         <div v-if="time" class="flex items-center gap-1.25 text-black/40 text-sm">

+ 2 - 2
packages/app/src/pages/home/components/home-banner.vue

@@ -2,7 +2,7 @@
 import ImageEvo from '@/components/image-evo.vue'
 import { unix } from 'dayjs'
 import { useRouter } from '../../../core/utils/router'
-import { updateSetIndexConfig} from '../../../core/libs/requests'
+
 const router = useRouter()
 
 const props = withDefaults(
@@ -29,7 +29,7 @@ const handleClick = async () => {
     // router.push(
     //   `/pages-sub/home/content/index?type=home-banner&id=${props.id}&data=${encodeURIComponent(JSON.stringify(props.item))}`,
     // )
-	await updateSetIndexConfig({id:props.id})
+	
 	router.push(
 	  `/pages-sub/home/content/index?type=home-banner&id=${props.id}`,
 	)

+ 5 - 1
packages/app/src/utils/http.ts

@@ -38,7 +38,7 @@ export const http = async <T>(options: CustomRequestOptions) => {
       // #endif
       // 响应成功
       success(res) {
-        // console.log(res)
+        console.log(res)
         // 状态码 2xx,参考 axios 的设计
         if (res.statusCode >= 200 && res.statusCode < 300) {
           // 2.1 提取核心数据 res.data
@@ -48,6 +48,10 @@ export const http = async <T>(options: CustomRequestOptions) => {
             userStore.clearUserInfo()
             uni.navigateTo({ url: '/pages-sub/login/index' })
             reject(res)
+          }  else if ((res.data as IResData<T>).code === 410) {
+            userStore.clearUserInfo()
+            uni.navigateTo({ url: '/pages-sub/login/index' })
+            reject(res)
           } else {
             !options.hideErrorToast &&
               uni.showToast({

BIN
packages/merchant/src/assets/pngs/email.png


BIN
packages/merchant/src/assets/pngs/local.png


BIN
packages/merchant/src/assets/pngs/phone.png


BIN
packages/merchant/src/assets/pngs/wechat.png


+ 9 - 1
packages/merchant/src/core/libs/agent-requests.ts

@@ -15,7 +15,8 @@ import {
   DesignerOrderSaleOther,
   BrowseRecordCountRes,
   BrowseRecord,
-  ReferralRes
+  ReferralRes,
+  FollowUpRes
 } from '@designer-hub/app/src/core/libs/models'
 import dayjs from 'dayjs'
 /**
@@ -329,6 +330,13 @@ export const getReferralDynamics = (stylistId: number) =>
     `/app-api/member/distribute/referralDynamics?referrerId=${stylistId}`,
   )
 /**
+ * 获取设计师跟进动态
+ */
+export const getFollowUpDynamics = (stylistId: number) =>
+  httpPost<FollowUpRes>(
+    `/app-api/member/app-broker/statisticsStylistFollowUp?stylistId=${stylistId}`,
+  )  
+/**
  * /app-api/member/browse-record/page 浏览记录
  * @param query
  */

+ 8 - 0
packages/merchant/src/core/libs/pngs.ts

@@ -14,6 +14,10 @@ import calculatorBg from '@/assets/pngs/calculator-bg.png'
 import linkBuckle from '@/assets/pngs/link-buckle.png'
 import memberTextV1 from '@/assets/pngs/member-text-v1.png'
 import referrerOne from '@/assets/pngs/referrer-1.png'
+import phone from '@/assets/pngs/phone.png'
+import wechat from '@/assets/pngs/wechat.png'
+import email from '@/assets/pngs/email.png'
+import local from '@/assets/pngs/local.png'
 
 export {
   scheduleCardBg,
@@ -31,5 +35,9 @@ export {
   calculatorBg,
   linkBuckle,
   memberTextV1,
+  phone,
+  wechat,
+  email,
+  local,
   referrerOne
 }

+ 15 - 0
packages/merchant/src/core/libs/requests.ts

@@ -187,6 +187,21 @@ export const getFollowStatistics = (query = {}) =>
 export const getBroker = (query: { brokerId: string }) =>
   httpGet<Agent>('/app-api/member/app-broker/get', query)
 /**
+ * 渠道端-获取经纪人个人名片
+ */
+export const getBrokerCard = (query: { brokerId: string }) =>
+  httpGet<Agent>('/app-api/member/card/get', query)
+/**
+ * 渠道端-获得名片样式
+ */
+export const cardStyleList = () =>
+  httpGet<Agent>('/app-api/member/card/listStyle')    
+/**
+ * 渠道端-获得名片样式
+ */
+export const cardStyleSave = (query = {}) =>
+  httpPost<Agent>('/app-api/member/card/create',query)    
+/**
  * 获取会员等级配置
  */
 export const getAppMemberLevelConfigs = () =>

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

@@ -16,6 +16,7 @@ import {
   getBrowseRecords,
   getBrowseRecordRemarks,
   getReferralDynamics,
+  getFollowUpDynamics,
 } from '@/core/libs/agent-requests'
 import {
   desinTopBg,
@@ -64,6 +65,9 @@ const { data: browseRecordCount, run: setBrowseRecordCount } = useRequest(() =>
 const { data: referralDynamics, run: setReferralDynamics } = useRequest(() =>
   getReferralDynamics(id.value),
 )
+const { data: followUpDynamics, run: setFollowUpDynamics } = useRequest(() =>
+  getFollowUpDynamics(id.value),
+)
 console.log('browseRecordCount data', data)
 const browseRecordCountItems = computed(() => [
   {
@@ -120,6 +124,26 @@ const referraItems = computed(() => [
     subValue: referralDynamics.value?.thisYearReferralBonus ?? 0,
   },
 ])
+const followUpItems = computed(() => [
+  {
+    title: '累积跟进次数',
+    subTitle: '本年',
+    value: followUpDynamics.value?.totality ?? 0,
+    subValue: followUpDynamics.value?.thisYearTotality ?? 0,
+  },
+  {
+    title: '线上跟进',
+    subTitle: '本年',
+    value: followUpDynamics.value?.online ?? 0,
+    subValue: followUpDynamics.value?.thisYearOnline ?? 0,
+  },
+  {
+    title: '线下拜访',
+    subTitle: '本年',
+    value: followUpDynamics.value?.offline ?? 0,
+    subValue: followUpDynamics.value?.thisYearOffline ?? 0,
+  },
+])
 const recentActivities = computed(() => [
   { label: '最近浏览品牌', value: '' },
   { label: '最近到店品牌', value: '' },
@@ -170,7 +194,7 @@ function formatDuration1(seconds) {
 onLoad(async (query) => {
   id.value = query?.id
   await setData()
-  await Promise.all([setBrowseRecordCount(),setReferralDynamics()])
+  await Promise.all([setBrowseRecordCount(),setReferralDynamics(),setFollowUpDynamics()])
   console.log(referralDynamics.value)
 })
 </script>
@@ -377,6 +401,45 @@ onLoad(async (query) => {
 		      </ListHelperEvo>
 		    </div>
 		  </Card>
+		  <Card custom-class="my-4">
+		    <div class="flex items-center justify-between">
+		      <div class="text-black/90 text-base font-normal font-['PingFang_SC'] leading-relaxed">
+		        跟进动态
+		      </div>
+		    </div>
+		    <div class="mt-3">
+		      <ListHelperEvo
+		        :content-class="`grid grid-cols-3 gap-2.5`"
+		        :items="followUpItems"
+		        custom-class="grid grid-cols-3 gap-2.5"
+		      >
+		        <template #default="{ item }">
+		          <div class="rounded-lg aspect-[1/1] flex flex-col justify-around p-2.5">
+		            <div
+		              class="text-black/60 text-xs font-normal font-['PingFang_SC'] leading-none"
+		            >
+		              {{ item.title }}
+		            </div>
+		            <div class="text-black/90 text-lg font-bold font-['D-DIN_Exp'] leading-normal">
+		              {{ item.value }}
+		            </div>
+		            <div class="flex items-center gap-1 whitespace-nowrap">
+		              <div
+		                class="text-black/40 text-xs font-normal font-['PingFang_SC'] leading-none"
+		              >
+		                {{ item.subTitle }}
+		              </div>
+		              <div
+		                class="text-black/90 text-xs font-normal font-['D-DIN_Exp'] leading-norma"
+		              >
+		                {{ item.subValue }}
+		              </div>
+		            </div>
+		          </div>
+		        </template>
+		      </ListHelperEvo>
+		    </div>
+		  </Card>
 
           <div class="my-4">
             <Card>
@@ -462,7 +525,7 @@ onLoad(async (query) => {
                       304958 -->
                       {{ it.remark }}
                     </div>
-                    <div class="mt-[15px] flex gap-2.5">
+                    <div class="mt-[15px] flex gap-2.5" v-if="it?.imgUrl">
                       <template v-for="(src, index) in it?.imgUrl?.split(',')" :key="index">
                         <wd-img
                           custom-class="rounded-lg overflow-hidden"

+ 107 - 62
packages/merchant/src/pages/mine/agent/business-card/edit-card.vue

@@ -7,7 +7,9 @@
 }
 </route>
 <script setup lang="ts">
+import { getBrokerCard, cardStyleList, cardStyleSave } from '../../../../core/libs/requests'
 import { businessLogo, logoWhite, edit, wechatGreen } from '@/core/libs/svgs'
+import { requestToast } from '@designer-hub/app/src/core/utils/common'
 import { NetImages } from '@/core/libs/net-images'
 import BottomAppBar from '@/components/bottom-app-bar.vue'
 import { storeToRefs } from 'pinia'
@@ -15,16 +17,19 @@ import { useUserStore } from '@/store/user'
 import DataForm from '@/components/data-form.vue'
 import { DataFormSchema } from '@/components/data-form'
 import { DesignerBasicInfo } from '@designer-hub/app/src/core/libs/models'
+import { omit } from 'radash'
 
 const { userInfo } = storeToRefs(useUserStore())
-const infos = ref<any[]>(['公司', '职位', '手机', '微信', '邮箱'])
+const infos = ref<any[]>([])
 const formData = ref({})
 const schema = ref<DataFormSchema>({
   avatar: {
-    type: 'TextField',
+    type: 'ImageUploader',
     label: '头像',
     labelWidth: 120,
-    props: undefined,
+    props: {
+		limit:1
+	},
   },
   name: {
     type: 'TextField',
@@ -53,75 +58,113 @@ const schema = ref<DataFormSchema>({
     labelWidth: 120,
   },
 })
-const submitUpload = () => {
-  console.log('submitUpload')
+const { data:cardData, run: setCardData } = useRequest(() =>
+  getBrokerCard({ brokerId: userInfo.value.userId!.toString() }),
+)
+const { data:styleData, run: setStyleData } = useRequest(() =>
+  cardStyleList(),
+)
+const changeStyle = (index) => {
+	cardData.value.cardStyleUrl = styleData.value[index].url
+	formData.value.cardStyleId = styleData.value[index].id
+}
+const infosChange = (index) => {
+	infos.value[index].status = !infos.value[index].status
 }
+const submitUpload = async () => {
+	console.log("1111")
+  const { code } = await requestToast(
+    () =>
+      cardStyleSave(formData.value),
+    { success: true, successTitle: '保存成功' },
+  )
+  if (code === 0) {
+    await setCardData()
+  }
+}
+onLoad(async () => {
+	await setCardData()
+	await setStyleData()
+	infos.value = cardData.value.displayInfo
+	formData.value = {
+	  ...omit(cardData.value, []),
+	};
+})
 </script>
 <template>
   <div class="p-[16px] bg-[#FFF]">
-    <div class="h-185px p-20px relative rounded-[10px] color-[#ffffff] bg-[#2357E9]">
-      <wd-img
-        :src="businessLogo"
-        width="78px"
-        height="27px"
-        mode="widthFix"
-        custom-class="absolute top-[-20px] left-[-24px]"
-      />
-      <div class="absolute top-[-1px] left-[0px] z-10">
-        <wd-img
-          :src="logoWhite"
-          width="17px"
-          height="17px"
-          round
-          custom-class="absolute top-[3px] left-[7px] bg-[#0cbe7d]"
-        />
-        <wd-img
-          :src="NetImages.筑巢荟"
-          width="40px"
-          height="17px"
-          custom-class="absolute top-[5px] left-[7px]"
-        />
-      </div>
-      <div class="h-65px flex justify-between items-center mb-10px">
-        <div class="font-size-22px fw-400">
-          李世东
-          <text class="font-size-12px ml-10px">平台经纪人</text>
-        </div>
-        <wd-img
-          :src="`https://image.zhuchaohui.com/zhucaohui/d0533ae0ab4fa6de2526e7088b346f5ff4c82a9e7b5622b75d9db3de64377471.jpg`"
-          round
-          width="65px"
-          height="65px"
-        />
-      </div>
-      <div class="flex items-center">
-        <wd-icon name="phone" size="15px" color="#ffffff" />
-        <div class="font-size-12px ml-10px">13888888888</div>
-      </div>
-      <div class="flex items-center">
-        <wd-icon name="phone" size="15px" color="#ffffff" />
-        <div class="font-size-12px ml-10px">13888888888</div>
-      </div>
-      <div class="flex items-center">
-        <wd-icon name="phone" size="15px" color="#ffffff" />
-        <div class="font-size-12px ml-10px">13888888888</div>
-      </div>
-      <div class="flex items-center">
-        <wd-icon name="phone" size="15px" color="#ffffff" />
-        <div class="font-size-12px ml-10px">13888888888</div>
-      </div>
+    <div class="h-185px p-20px relative rounded-[10px] color-[#ffffff] bg-[#2357E9] overflow-hidden">
+	  <div class="absolute w-full h-full top-0 left-0 z-0">
+		  <wd-img :src="cardData.cardStyleUrl" custom-class="w-full h-full"></wd-img>
+	  </div>	
+      <!-- <div class="absolute z-10"> -->
+		  <wd-img
+		    :src="businessLogo"
+		    width="78px"
+		    height="27px"
+		    mode="widthFix"
+		    custom-class="absolute top-[-20px] left-[-24px]"
+		  />
+		  <div class="absolute top-[-1px] left-[0px] z-10">
+		    <wd-img
+		      :src="logoWhite"
+		      width="17px"
+		      height="17px"
+		      round
+		      custom-class="absolute top-[3px] left-[7px] bg-[#0cbe7d]"
+		    />
+		    <wd-img
+		      :src="NetImages.筑巢荟"
+		      width="40px"
+		      height="17px"
+		      custom-class="absolute top-[5px] left-[7px]"
+		    />
+		  </div>
+		  <div class="h-65px flex justify-between items-center mb-10px relative">
+		    <div class="font-size-22px fw-400">
+		      {{ cardData.name }}
+		      <text class="font-size-12px ml-10px" v-if="infos[1].status">{{ cardData.position }}</text>
+		    </div>
+		    <wd-img
+		      :src="cardData.avatar"
+		      round
+		      width="65px"
+		      height="65px"
+		    />
+		  </div>
+		  <div class="flex items-center relative">
+		    <wd-icon name="phone" size="15px" color="#ffffff" />
+		    <div class="font-size-12px ml-10px">{{ cardData.phone }}</div>
+		  </div>
+		  <div class="flex items-center relative" v-if="infos[4].status">
+		    <wd-icon name="phone" size="15px" color="#ffffff" />
+		    <div class="font-size-12px ml-10px">{{ cardData.weChat }}</div>
+		  </div>
+		  <div class="flex items-center relative" v-if="infos[2].status">
+		    <wd-icon name="phone" size="15px" color="#ffffff" />
+		    <div class="font-size-12px ml-10px">{{ cardData.email }}</div>
+		  </div>
+		  <div class="flex items-center relative" v-if="infos[3].status">
+		    <wd-icon name="phone" size="15px" color="#ffffff" />
+		    <div class="font-size-12px ml-10px">{{ cardData.address }}</div>
+		  </div>
+	  <!-- </div> -->
     </div>
     <!-- 名片样式 -->
     <div
       class="mt-24px mb-18px line-height-26px fw-400 color-[rgba(0,0,0,0.85)] font-size-16px flex justify-between items-center"
     >
       <div>名片样式</div>
-      <wd-icon name="arrow-right" size="22px"></wd-icon>
+      <!-- <wd-icon name="arrow-right" size="22px"></wd-icon> -->
     </div>
     <div class="flex justify-between items-center">
-      <div class="w-76px h-42px border-[1px] border-solid border-[#E2E4ED]"></div>
-      <div class="w-76px h-42px border-[1px] border-solid border-[#E2E4ED]"></div>
-      <div class="w-76px h-42px border-[1px] border-solid border-[#E2E4ED]"></div>
+      <div class="w-76px h-42px border-[1px] border-solid border-[#E2E4ED]" v-for="(it,i) in styleData" :key="i" @click="changeStyle(i)">
+		  <wd-img
+		    :src="it.url" 
+		    width="76px"
+		    height="42px"
+		  />
+	  </div>
     </div>
     <!-- 展示信息 -->
     <div
@@ -131,11 +174,13 @@ const submitUpload = () => {
     </div>
     <div class="pt-[30px] pb-[15px] mb-[15px] flex items-center overflow-x-auto">
       <div
-        class="selectItem inline-block px-18px py-4px rounded-[2px] bg-[#F3F6FF] mr-10px whitespace-nowrap font-size-14px lh-[24px] fw-400"
+        class="selectItem inline-block px-18px py-4px rounded-[2px] mr-10px whitespace-nowrap font-size-14px lh-[24px] fw-400"
+		:class="it.status?'bg-[#2357E9] text-white':'bg-[#F3F6FF]'"
         v-for="(it, index) in infos"
         :key="index"
+		@click="infosChange(index)"
       >
-        {{ it }}
+        {{ it.name }}
       </div>
     </div>
     <wd-gap bg-color="#F2F3F6"></wd-gap>

+ 92 - 16
packages/merchant/src/pages/mine/agent/business-card/index.vue

@@ -7,10 +7,23 @@
 }
 </route>
 <script setup lang="ts">
+import { getBrokerCard } from '../../../../core/libs/requests'
+import { useUserStore } from '../../../../store/index'
+import { Canvas } from '@designer-hub/app/src/core/utils/canvas'
 import { businessLogo, logoWhite, edit, wechatGreen } from '@/core/libs/svgs'
+import { phone, wechat, email, local } from '@/core/libs/pngs'
 import { NetImages } from '@/core/libs/net-images'
 import BottomAppBar from '@/components/bottom-app-bar.vue'
-
+const currentInstance = getCurrentInstance()
+import { storeToRefs } from 'pinia'
+const userStore = useUserStore()
+const { userInfo } = storeToRefs(userStore)
+const { data, run: setData } = useRequest(() =>
+  getBrokerCard({ brokerId: userInfo.value.userId!.toString() }),
+)
+const wechatShare = () =>{
+	
+}
 const submitUpload = () => {
   console.log('submitUpload')
 }
@@ -19,11 +32,70 @@ const toEditPage = () => {
     url: '/pages/mine/agent/business-card/edit-card',
   })
 }
+onLoad(async () => {
+	await setData()
+	// try {
+	//   const [bg, bLogo, logoW, icon1, icon2, icon3 ,icon4, icon5, avatar] =
+	//     await Promise.all(
+	//       [
+	//         data.value.cardStyleUrl || '',
+	// 		businessLogo,
+	// 		logoWhite,
+	// 		phone,
+	// 		wechat,
+	// 		email,
+	// 		local,
+	// 		NetImages.筑巢荟,
+	//         data.value.avatar || ''
+	//       ].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.Image(logoW, 0, 0, 13, 13)
+	//   canvas.Image(bLogo, 10, 4, 78, 27)	
+	//   canvas.Image(icon1, 28, 69, 15, 15)
+	//   canvas.Image(icon2, 24, 24, 56, 80)
+	//   canvas.FillText(data.value.phone, '#ffffff', 12, 46, 80)
+	  
+	//   canvas.CircleImage(avatar, 220, 40, 37)
+	  
+	//   canvas.RoundRect('#F5F5F5', 26, 282, 300, 306, 20)
+	
+	//   ctx.draw && ctx.draw()
+	// } catch (e) {
+	//   console.log(e)
+	// }
+})
 </script>
 <template>
   <div class="p-[16px]">
     <div class="h-325px relative rounded-[10px] color-[#ffffff] bg-[#f3f6ff]">
-      <div class="h-185px p-20px relative rounded-[10px] color-[#ffffff] bg-[#2357E9]">
+     <div class="h-185px p-20px relative rounded-[10px] color-[#ffffff] bg-[#2357E9] overflow-hidden">
+		<div class="absolute w-full h-full top-0 left-0 z-0">
+			<wd-img :src="data.cardStyleUrl" custom-class="w-full h-full"></wd-img>
+		</div> 
         <wd-img
           :src="businessLogo"
           width="78px"
@@ -46,32 +118,32 @@ const toEditPage = () => {
             custom-class="absolute top-[5px] left-[7px]"
           />
         </div>
-        <div class="h-65px flex justify-between items-center mb-10px">
+        <div class="h-65px flex justify-between items-center mb-10px relative">
           <div class="font-size-22px fw-400">
-            李世东
-            <text class="font-size-12px ml-10px">平台经纪人</text>
+            {{ data.name }}
+            <text class="font-size-12px ml-10px" v-if="data.displayInfo[1].status">{{ data.position }}</text>
           </div>
-          <wd-img :src="businessLogo" round width="65px" height="65px" />
+          <wd-img :src="data.avatar" round width="65px" height="65px" />
         </div>
-        <div class="flex items-center">
+        <div class="flex items-center relative z-10">
           <wd-icon name="phone" size="15px" color="#ffffff" />
-          <div class="font-size-12px ml-10px">13888888888</div>
+          <div class="font-size-12px ml-10px">{{ data.phone }}</div>
         </div>
-        <div class="flex items-center">
+        <div class="flex items-center relative" v-if="data.displayInfo[4].status">
           <wd-icon name="phone" size="15px" color="#ffffff" />
-          <div class="font-size-12px ml-10px">13888888888</div>
+          <div class="font-size-12px ml-10px">{{ data.weChat }}</div>
         </div>
-        <div class="flex items-center">
+        <div class="flex items-center relative" v-if="data.displayInfo[2].status">
           <wd-icon name="phone" size="15px" color="#ffffff" />
-          <div class="font-size-12px ml-10px">13888888888</div>
+          <div class="font-size-12px ml-10px">{{ data.email }}</div>
         </div>
-        <div class="flex items-center">
+        <div class="flex items-center relative" v-if="data.displayInfo[3].status">
           <wd-icon name="phone" size="15px" color="#ffffff" />
-          <div class="font-size-12px ml-10px">13888888888</div>
+          <div class="font-size-12px ml-10px">{{ data.address }}</div>
         </div>
       </div>
-      <div class="h-75px pt-[25px] px-[12px] color-[#7F88A0] font-size-12px fw-400 lh-[19px]">
-        你好,我是筑巢荟平台经纪人李世东,这个是我的名片,请惠存!
+      <div class="h-75px pt-[25px] px-[12px] color-[#7F88A0] font-size-12px fw-400 lh-[19px]" v-if="data.displayInfo[5].status">
+        {{ data.personalBio }}
       </div>
     </div>
     <!-- 按钮 -->
@@ -85,6 +157,7 @@ const toEditPage = () => {
       </div>
       <div
         class="inline-flex py-[10px] px-[24px] rounded-[8px] border-[1px] border-solid border-[#E2E4ED] color-[#13AC5C] lh-[24px]"
+		@click="wechatShare"
       >
         <wd-img
           :src="wechatGreen"
@@ -109,5 +182,8 @@ const toEditPage = () => {
         保存到本地
       </wd-button>
     </BottomAppBar>
+	<div class="w-[344px] h-[185px] fixed top-0 left-0">
+	  <canvas class="w-full h-full" id="qrcode" canvas-id="qrcode"></canvas>
+	</div>
   </div>
 </template>

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

@@ -132,14 +132,14 @@ onMounted(async () => {
           <!--            ID:{{ agent?.inviteCode }}-->
           <!--          </div>-->
         </div>
-        <!-- <div class="flex flex-col items-center mr-[30px]" @click.stop="toBusinessCard">
+        <div class="flex flex-col items-center mr-[30px]" @click.stop="toBusinessCard">
           <div class="w-[29px] h-[29px] relative">
             <wd-img width="28" height="28" :src="business"></wd-img>
           </div>
           <div class="text-white text-[10px] font-normal font-['PingFang_SC'] leading-relaxed">
             个人名片
           </div>
-        </div> -->
+        </div>
         <div class="flex flex-col items-center" @click.stop="toInvite">
           <div class="w-[29px] h-[29px] relative">
             <wd-img width="28" height="28" :src="qrCode"></wd-img>
@@ -335,7 +335,7 @@ onMounted(async () => {
                   304958 -->
                   {{ it.remark }}
                 </div>
-                <div class="mt-[15px] flex gap-2.5">
+                <div class="mt-[15px] flex gap-2.5" v-if="it?.imgUrl">
                   <template v-for="(src, index) in it?.imgUrl?.split(',')" :key="index">
                     <wd-img
                       custom-class="rounded-lg overflow-hidden"