diff --git a/src/components/partner-banner/index.tsx b/src/components/partner-banner/index.tsx index 2109357..5ee7bd5 100644 --- a/src/components/partner-banner/index.tsx +++ b/src/components/partner-banner/index.tsx @@ -61,7 +61,7 @@ export default function PartnerBanner() {
{visible && ( - setVisible(false)} onSuccess={handleBind} needPhone={needPhone} /> + setVisible(false)} onSuccess={handleBind} needPhone={needPhone} /> )} ); diff --git a/src/components/partner-intro/index.tsx b/src/components/partner-intro/index.tsx index caadc0c..52b797e 100644 --- a/src/components/partner-intro/index.tsx +++ b/src/components/partner-intro/index.tsx @@ -1,19 +1,19 @@ import { Button, Canvas } from '@tarojs/components'; import Taro from '@tarojs/taro'; -import { useCallback, useState } from 'react'; +import { useCallback } from 'react'; import { openCustomerServiceChat } from '@/utils/common'; -import { getPartnerQrcode } from '@/utils/partner'; +import { getCouponQrCode, generateMembershipCoupon } from '@/utils/coupon'; import './index.less'; const PREFIX = 'partner-intro'; export default function PartnerIntro() { - const [posterPath, setPosterPath] = useState(''); const getQrcode = async () => { try { - const data = await getPartnerQrcode(); // 假设 getPartnerQrcode 返回 ArrayBuffer + const { code } = await generateMembershipCoupon(); + const data = await getCouponQrCode(code); const base64 = Taro.arrayBufferToBase64(data); return `data:image/png;base64,${base64}`; } catch (error) { @@ -36,11 +36,9 @@ export default function PartnerIntro() { height: 2668, // 实际绘制高度 destWidth: 750, // 目标显示宽度 destHeight: 1334, // 目标显示高度 - fileType: 'jpg', + fileType: 'png', }); - setPosterPath(tempFilePath.tempFilePath); - resolve(tempFilePath.tempFilePath); }); } catch (error) { @@ -57,21 +55,21 @@ export default function PartnerIntro() { const canvas = res[0].node; const ctx = canvas.getContext('2d'); - canvas.width = 1500; - canvas.height = 2668; + canvas.width = 550 * 2; + canvas.height = 918 * 2; ctx.scale(2, 2); // 绘制背景图片 const bgImage = canvas.createImage(); - const poster = 'https://publiccdn.neighbourhood.com.cn/img/poster.png' + const poster = 'https://publiccdn.neighbourhood.com.cn/img/share-coupon-poster.png'; bgImage.src = poster; bgImage.onload = () => { - ctx.drawImage(bgImage, 0, 0, 750, 1334); + ctx.drawImage(bgImage, 0, 0, 550, 918); const qrCodeImage = canvas.createImage(); qrCodeImage.src = qrCode; // 假设 getQrcode() 返回的是二维码图片的路径 qrCodeImage.onload = () => { - ctx.drawImage(qrCodeImage, 235, 894, 280, 280); // 绘制二维码,位置和大小 + ctx.drawImage(qrCodeImage, 196, 600, 160, 160); // 绘制二维码,位置和大小 saveCanvasToTempFile().then(tempPath => { resolve(tempPath); }); @@ -84,13 +82,10 @@ export default function PartnerIntro() { }); }; const savePoster = async () => { - let filePath = posterPath; - if (!filePath) { - Taro.showLoading({ title: '正在生成海报' }); - const qrCode = await getQrcode(); - filePath = await drawCanvas(qrCode); - Taro.hideLoading(); - } + Taro.showLoading({ title: '正在生成海报' }); + const qrCode = await getQrcode(); + const filePath = await drawCanvas(qrCode); + Taro.hideLoading(); const res = await Taro.getSetting(); const hasPermission = res.authSetting['scope.writePhotosAlbum']; @@ -144,7 +139,9 @@ export default function PartnerIntro() {
分享方法
-
分享小程序任意页面到群、朋友圈、好友即可
+
+ 点击底部按钮向好友群友赠送会员,或者分享通告列表、通告详情等页面给朋友即可 +
@@ -163,10 +160,10 @@ export default function PartnerIntro() {
diff --git a/src/http/api.ts b/src/http/api.ts index a29d67b..024cd42 100644 --- a/src/http/api.ts +++ b/src/http/api.ts @@ -1,4 +1,4 @@ -// export const DOMAIN = 'http://192.168.60.191:8082'; +// export const DOMAIN = 'http://192.168.60.116:8082'; export const DOMAIN = process.env.BRAND === 'dev' ? 'https://dev.neighbourhood.cn' : 'https://neighbourhood.cn'; // export const DOMAIN = 'https://dev.neighbourhood.cn'; export const BASE_URL = `${DOMAIN}/api`; @@ -85,4 +85,7 @@ export enum API { GET_PROFIT_STAT = '/user/profits', WITHDRAW = '/user/withdraw', GET_WITHDRAW_LIST = '/user/withdraw/records', + GENERATE_MEMBERSHIP_COUPON = '/coupon/membership/generate', + CLAIM_MEMBERSHIP_COUPON = '/coupon/membership/claim', + GET_VIP_QRCODE = '/user/getVipQrCode', } diff --git a/src/pages/give-vip/index.less b/src/pages/give-vip/index.less index d38210c..ae6af81 100644 --- a/src/pages/give-vip/index.less +++ b/src/pages/give-vip/index.less @@ -1 +1,130 @@ -.give-vip {} +@import '@/styles/variables.less'; +@import '@/styles/common.less'; + +.give-vip { + padding: 40px 24px 200px 24px; + &__hint { + font-size: 28px; + line-height: 48px; + color: @blColorG2; + gap: 12px; + .flex-row(); + justify-content: center; + } + &__icon { + width: 65px; + height: 65px; + } + &__title { + margin-top: 24px; + font-weight: 500; + font-size: 40px; + line-height: 48px; + color: @blColor; + text-align: center; + flex: 0 1 auto; + gap: 12px; + .flex-row(); + justify-content: center; + .highlight { + margin-left: -12px; + display: inline-block; + color: @blHighlightColor; + } + } + &__bg { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + } + &__info { + &-block { + margin-top: 48px; + } + &-title { + font-weight: 500; + font-size: 32px; + line-height: 32px; + color: #1d2129; + margin-bottom: 24px; + } + &-card { + box-sizing: border-box; + background: #ffffff; + border-radius: 24px; + padding: 32px; + } + &-body { + font-weight: 400; + font-size: 28px; + line-height: 40px; + color: #333333; + margin-bottom: 32px; + } + &-img { + width: 100%; + height: 614px; + } + } + &__footer { + position: fixed; + bottom: 0; + width: 100%; + padding: 12px 32px; + box-shadow: 0px -4px 20px 0px #00000014; + box-sizing: border-box; + background: #ffffff; + } + + &__footer-body { + .flex-row(); + justify-content: center; + } + &__button { + .button(@height: 88px; @fontSize: 32px; @fontWeight: 500; @borderRadius: 44px;); + width: 400px; + } + &__coupon { + &-info { + position: relative; + margin-top: 32px; + padding: 24px; + box-sizing: border-box; + border-radius: 24px; + + .flex-column(); + justify-content: center; + } + + &-title { + font-weight: 500; + font-size: 32px; + line-height: 48px; + color: @blHighlightColor; + margin-bottom: 8px; + } + &-intro { + font-size: 28px; + line-height: 40px; + margin-bottom: 12px; + color: @blColor; + gap: 4px; + .flex-row(); + .highlight { + font-weight: 600; + font-size: 32px; + line-height: 56px; + color: @blHighlightColor; + } + } + &-valid { + font-size: 28px; + line-height: 40px; + + color: @blColorG2; + } + } +} diff --git a/src/pages/give-vip/index.tsx b/src/pages/give-vip/index.tsx index 0febdb8..2d62e47 100644 --- a/src/pages/give-vip/index.tsx +++ b/src/pages/give-vip/index.tsx @@ -1,12 +1,103 @@ -import { Button } from '@taroify/core'; +import { Image, Button } from '@tarojs/components'; +import { useLoad } from '@tarojs/taro'; +import { Fragment, useCallback, useState } from 'react'; + +import SafeBottomPadding from '@/components/safe-bottom-padding'; +import { PageUrl } from '@/constants/app'; import { logWithPrefix } from '@/utils/common'; - +import { claimMembershipCoupon, getCouponCodeFromQuery } from '@/utils/coupon'; +import { getPageQuery, switchTab } from '@/utils/route'; +import { formatTime } from '@/utils/time'; import './index.less'; const PREFIX = 'give-vip'; const log = logWithPrefix(PREFIX); -export default function GiveVip() { - return
; +enum State { + PENDING, + SUCCESS, + FAILED, +} +export default function GiveVip() { + const [state, setState] = useState(State.PENDING); + const [expireAt, setExpireAt] = useState(''); + const [usedBefore, setUsedBefore] = useState(false); + const handleNavigate = useCallback(() => { + switchTab(PageUrl.Job); + }, []); + + useLoad(() => { + const query = getPageQuery<{ d: string }>(); + const code = getCouponCodeFromQuery(query); + claimMembershipCoupon(code!) + .then(res => { + setUsedBefore(res.usedBefore); + setExpireAt(res.usageExpireAt); + setState(Date.now() > new Date(res.usageExpireAt).getTime() ? State.FAILED : State.SUCCESS); + }) + .catch(err => { + console.error(err); + setState(State.FAILED); + }); + }); + + return ( +
+ {state === State.SUCCESS && ( + +
+ +
{usedBefore ? '宝子,你已经领过了,下周再来' : '宝子,播络会员领取成功'}
+
+
+ +
播络日会员
+
+ 会员有效期内可增加
10次
报单机会 +
+
+ 有效期至:{formatTime(expireAt, 'YYYY/MM/DD HH:mm:ss', false)} +
+
+
+ )} + {state === State.FAILED && ( + +
+ +
宝子,链接已失效
+
+
+ 请联系分享人
再次分享
+
+
+ )} +
+
播络简介
+
+
+ 播络通告整理汇集了本地每日新增带货主播通告,方便大家及时找到高薪工作 +
+ +
+
+
+
+ +
+ +
+
+ ); } diff --git a/src/pages/partner/index.tsx b/src/pages/partner/index.tsx index ffde97f..03701e9 100644 --- a/src/pages/partner/index.tsx +++ b/src/pages/partner/index.tsx @@ -6,7 +6,9 @@ import { useState } from 'react'; import PartnerIntro from '@/components/partner-intro'; import PartnerInviteList from '@/components/partner-invite-list'; import PartnerProfit from '@/components/partner-profit'; +import { PageUrl } from '@/constants/app'; import useInviteCode from '@/hooks/use-invite-code'; +import { generateMembershipCoupon } from '@/utils/coupon'; import { getCommonShareMessage } from '@/utils/share'; import './index.less'; @@ -18,9 +20,17 @@ export default function Partner() { const handleChange = v => { setTab(v); }; - useShareAppMessage(() => { + useShareAppMessage(async () => { console.log('Partner inviteCode', inviteCode); - return getCommonShareMessage({ useCapture: false, inviteCode }); + const { code } = await generateMembershipCoupon(); + return getCommonShareMessage({ + useCapture: false, + inviteCode, + title: '宝子,送你个播络会员,免费找主播工作', + path: PageUrl.GiveVip, + params: { d: code }, + imageUrl: 'https://publiccdn.neighbourhood.com.cn/img/share-coupon1.png', + }); }); return ( diff --git a/src/types/coupon.ts b/src/types/coupon.ts new file mode 100644 index 0000000..61e4dbe --- /dev/null +++ b/src/types/coupon.ts @@ -0,0 +1,11 @@ +export type ClaimMembershipCouponResponse = { + code: string; + usedBefore: boolean; + usageExpireAt: string; +}; + +export type GenerateMembershipCouponResponse = { + code: string; + linkExpireAt: string; + shareUrl: string; +}; diff --git a/src/utils/coupon.ts b/src/utils/coupon.ts new file mode 100644 index 0000000..ef0dc2f --- /dev/null +++ b/src/utils/coupon.ts @@ -0,0 +1,31 @@ +import http from '@/http'; +import { API } from '@/http/api'; +import { ClaimMembershipCouponResponse, GenerateMembershipCouponResponse } from '@/types/coupon'; + +export const generateMembershipCoupon = async () => { + return await http.post(API.GENERATE_MEMBERSHIP_COUPON); +}; +export const claimMembershipCoupon = async (code: string) => { + return await http.post(API.CLAIM_MEMBERSHIP_COUPON, { + contentType: 'application/x-www-form-urlencoded', + data: { code }, + }); +}; +export const getCouponQrCode = async (code: string) => { + return await http.post(API.GET_VIP_QRCODE, { + contentType: 'application/x-www-form-urlencoded', + responseType: 'arraybuffer', + data: { code }, + }); +}; +export const getCouponCodeFromQuery = (query: Record): string | undefined => { + if (query) { + if (query.scene) { + return query.scene.replace('d%3D', ''); + } + if (query.d) { + return query.d; + } + } + return undefined; +}; diff --git a/src/utils/share.ts b/src/utils/share.ts index c07ca9d..1768a2e 100644 --- a/src/utils/share.ts +++ b/src/utils/share.ts @@ -20,6 +20,7 @@ interface ShareAppProps { inviteCode?: string; title?: string; path?: PageUrl; + imageUrl?: string; params?: Record; } @@ -28,6 +29,7 @@ export const getCommonShareMessage = ({ inviteCode, title, path, + imageUrl: _imageUrl, params = {}, }: ShareAppProps = {}): ShareAppMessageReturn => { const inviteParams = inviteCode ? { c: inviteCode } : undefined; @@ -36,6 +38,6 @@ export const getCommonShareMessage = ({ return { title: title || `昨天新增了${getRandomCount()}条主播通告,宝子快来看看`, path: sharePath, - imageUrl: useCapture ? undefined : imageUrl, + imageUrl: useCapture ? undefined : _imageUrl || imageUrl, }; }; diff --git a/src/utils/time.ts b/src/utils/time.ts index 7db1026..e5246f6 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -1,10 +1,12 @@ import dayjs from 'dayjs'; -export function formatTime(time: number | string, template = 'YYYY-MM-DD'): string { +export function formatTime(time: number | string, template = 'YYYY-MM-DD', toNum = true): string { if (!time) { return ''; } - time = Number(time); + if (toNum) { + time = Number(time); + } return dayjs(time).format(template); }