✨ feat(邀请码): 完成邀请码模块
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
export default {
|
||||
navigationBarTitleText: '邀请码',
|
||||
navigationBarTextStyle: 'black',
|
||||
navigationBarBackgroundColor: '#E4EEFD',
|
||||
backgroundColor: '#E4EEFD',
|
||||
backgroundColorTop: '#E4EEFD',
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
page {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
height: 100%;
|
||||
}
|
||||
.main {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
height: 100%;
|
||||
background: linear-gradient(to bottom, #E4EEFD 25%, $color_bg_one 42%);
|
||||
padding-bottom: 0;
|
||||
box-sizing: border-box;
|
||||
overflow-y: scroll;
|
||||
.content{
|
||||
flex: 1 1 auto;
|
||||
overflow: scroll;
|
||||
}
|
||||
}
|
||||
.background {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
padding: 20px 56px;
|
||||
padding-bottom: 0;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
overflow: hidden;
|
||||
.left{
|
||||
.title{
|
||||
padding-bottom: 15px;
|
||||
font-size: 60px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.description{
|
||||
font-size: 28px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
.right{
|
||||
.iconContainer{
|
||||
width: 260px;
|
||||
position: relative;
|
||||
bottom: -36px;
|
||||
.icon{
|
||||
width: 130%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.codeBar{
|
||||
border-radius: 15px;
|
||||
background-color: #f7f8fa;
|
||||
padding: 40px 0;
|
||||
.inviteCodeBar{
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
.invite{
|
||||
padding: 0 40px;
|
||||
font-size: 46px;
|
||||
font-weight: 500;
|
||||
color: #337FFF;
|
||||
line-height: 65px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.tips{
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
font-weight: 400;
|
||||
color: #9fa0a1;
|
||||
line-height: 28px;
|
||||
padding: 0 40px;
|
||||
}
|
||||
.inviteListTitle{
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 30px;
|
||||
.listTitle{
|
||||
padding: 0 20px;
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
color: #000000;
|
||||
}
|
||||
.titleIconLeft{
|
||||
width: 24px;
|
||||
height: 4px;
|
||||
background: linear-gradient(270deg, #333333 0%, rgba(51,51,51,0) 100%);
|
||||
opacity: 0.3;
|
||||
}
|
||||
.titleIconRight{
|
||||
width: 24px;
|
||||
height: 4px;
|
||||
background: linear-gradient(270deg, rgba(51,51,51,0) 0%, #333333 100%);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
}
|
||||
.bottomBar {
|
||||
flex: none;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 24px;
|
||||
padding-right: 24px;
|
||||
padding-left: 24px;
|
||||
background-color: #ffffff;
|
||||
padding-bottom: calc(20px + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(20px + env(safe-area-inset-bottom));
|
||||
}
|
||||
.codePreview{
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
.imageContainer{
|
||||
width: 80vw;
|
||||
height: auto;
|
||||
.image{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.previewTips{
|
||||
position: absolute;
|
||||
bottom: -80px;
|
||||
font-size: 40px;
|
||||
font-weight: 400;
|
||||
color: #c2c2c2;
|
||||
letter-spacing: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
import { Canvas, Image, Text, View } from '@tarojs/components'
|
||||
import Taro, { useReady } from '@tarojs/taro'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import style from './index.module.scss'
|
||||
import inviteCodePng from './inviteCode.png'
|
||||
import QRcode from './inviteCodePopup.png'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import LayoutBlock from '@/components/layoutBlock'
|
||||
import Divider from '@/components/divider'
|
||||
import type { TablePropsType } from '@/components/table'
|
||||
import Table from '@/components/table'
|
||||
import NormalButton from '@/components/normalButton'
|
||||
import { alert } from '@/common/common'
|
||||
import { GenBarCodeOrQrCode, GetInvitationInfo } from '@/api'
|
||||
import { getCDNSource } from '@/common/constant'
|
||||
// 需要传进来的表头数据示例
|
||||
const inviteColumns = [
|
||||
{
|
||||
key: 'invitee',
|
||||
title: '被邀请人',
|
||||
dataIndex: 'invitee',
|
||||
width: '50%',
|
||||
},
|
||||
{
|
||||
key: 'InviteResults',
|
||||
title: '邀请进度',
|
||||
dataIndex: 'InviteResults',
|
||||
width: '50%',
|
||||
},
|
||||
]
|
||||
// 邀请码
|
||||
const InviteCode = () => {
|
||||
const { fetchData } = GetInvitationInfo()
|
||||
const { fetchData: genCode } = GenBarCodeOrQrCode()
|
||||
const [inviteInfo, setInviteInfo] = useState<any>({})
|
||||
const [currentTable, setCurrentTable] = useState<TablePropsType>({
|
||||
columns: inviteColumns,
|
||||
dataSource: { list: [], total: 0 },
|
||||
})
|
||||
// 获取邀请码
|
||||
const getInviteCode = async() => {
|
||||
const res = await fetchData()
|
||||
if (res.success) {
|
||||
console.log('getInviteCode', res)
|
||||
setCurrentTable((prev: any) => ({
|
||||
...prev,
|
||||
dataSource: {
|
||||
list: res.data.invitation_record.map((item, index: number) => ({
|
||||
key: index,
|
||||
index: index + 1,
|
||||
invitee: item.name || '--',
|
||||
InviteResults: '已邀请',
|
||||
})),
|
||||
total: res.data.invitation_record.length,
|
||||
},
|
||||
}))
|
||||
setInviteInfo(res.data)
|
||||
}
|
||||
}
|
||||
// 生成二维码
|
||||
const genQRcode = async() => {
|
||||
const res = await genCode({ content: inviteInfo.invitation_code })
|
||||
if (res.success) {
|
||||
return res.data.qrcode_base64
|
||||
}
|
||||
}
|
||||
|
||||
const [, setForceUpdate] = useState({})
|
||||
const canvasNode = useRef<any>()
|
||||
const ctx = useRef<any>(null)
|
||||
const [showPopup, setShowPopup] = useState(false)
|
||||
|
||||
const [targetImageUrl, setTargetImageUrl] = useState('')
|
||||
|
||||
const getImageObject = (canvas, src) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log('getImageObject param', canvas, src)
|
||||
const img = canvas.createImage()
|
||||
img.src = src
|
||||
img.onload = () => {
|
||||
console.log('image===>', img)
|
||||
resolve(img)
|
||||
}
|
||||
img.onerror = (err) => {
|
||||
console.log('image error===>', err)
|
||||
alert.error('图片加载失败')
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
// canvas 生成 图片
|
||||
const saveCanvasToImage = (canvas) => {
|
||||
Taro.canvasToTempFilePath({
|
||||
canvas,
|
||||
fileType: 'png',
|
||||
success: (res) => {
|
||||
console.log('tempFilePath', res.tempFilePath)
|
||||
setTargetImageUrl(res.tempFilePath)
|
||||
},
|
||||
})
|
||||
}
|
||||
const getImageInfo = (filePath) => {
|
||||
Taro.getImageInfo({
|
||||
src: filePath,
|
||||
success: (infoRes) => {
|
||||
console.log('', infoRes)
|
||||
},
|
||||
})
|
||||
}
|
||||
// 初始化 canvas
|
||||
const initCanvas = async() => {
|
||||
Taro.nextTick(() => {
|
||||
const query = Taro.createSelectorQuery()
|
||||
query.select('#canvas').node(({ node: canvas }: { node: Taro.Canvas }) => {
|
||||
console.log('canvas==>', canvas)
|
||||
const context = canvas.getContext('2d')
|
||||
console.log('ctx', context)
|
||||
canvasNode.current = canvas
|
||||
ctx.current = context
|
||||
console.log('canvas', canvas)
|
||||
setForceUpdate({})
|
||||
}).exec()
|
||||
})
|
||||
}
|
||||
const startPaint = async(ctx, canvas, image) => {
|
||||
console.log('startPaint param', ctx, canvas, image)
|
||||
// 开始绘制
|
||||
const cCanvasCtx = ctx as any
|
||||
cCanvasCtx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
cCanvasCtx.drawImage(image, 0, 0, canvas.width, canvas.height)
|
||||
cCanvasCtx.save()
|
||||
cCanvasCtx.font = `${40}px 微软雅黑`
|
||||
cCanvasCtx.fillStyle = '#000000'
|
||||
cCanvasCtx.fillText('蜘蛛管家', 40, 80) // text up above canvas
|
||||
cCanvasCtx.save()
|
||||
cCanvasCtx.font = `${26}px 微软雅黑`
|
||||
cCanvasCtx.fillStyle = '#8f9398'
|
||||
cCanvasCtx.fillText('真挚邀请您建立合作关系', 40, 130) // text up above canvas
|
||||
cCanvasCtx.save()
|
||||
cCanvasCtx.font = `${24}px 微软雅黑`
|
||||
cCanvasCtx.fillStyle = '#a6a6a6'
|
||||
cCanvasCtx.fillText('请前往邀请码页面,进行扫描邀请', 100, 630) // text up above canvas
|
||||
cCanvasCtx.save()
|
||||
cCanvasCtx.font = `${36}px 微软雅黑`
|
||||
cCanvasCtx.fillStyle = '#7f7f7f'
|
||||
cCanvasCtx.fillText('邀 请 码', 72, 730) // text up above canvas
|
||||
cCanvasCtx.save()
|
||||
cCanvasCtx.font = `${24}px 微软雅黑`
|
||||
cCanvasCtx.fillStyle = '#cccccc'
|
||||
cCanvasCtx.fillText('|', 258, 724) // text up above canvas
|
||||
cCanvasCtx.save()
|
||||
cCanvasCtx.font = `${36}px 微软雅黑`
|
||||
cCanvasCtx.fillStyle = '#7f7f7f'
|
||||
cCanvasCtx.fillText(`${inviteInfo.invitation_code}`, 311, 730) // text up above canvas
|
||||
cCanvasCtx.save()
|
||||
const codeUrl = await genQRcode()
|
||||
try {
|
||||
const code: any = await getImageObject(canvas, codeUrl)
|
||||
cCanvasCtx.drawImage(code, 110, 213, 342, 342)
|
||||
}
|
||||
catch (err) {
|
||||
console.error('合成二维邀请码失败')
|
||||
}
|
||||
|
||||
saveCanvasToImage(canvas)
|
||||
}
|
||||
// 绘制最终的海报,图片使用两倍大小,canvas大小是图片的二分之一 就能让canvas生成出来的图片清晰了
|
||||
const drawPictorial = async() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!ctx.current) {
|
||||
// 重新初始化canvas
|
||||
initCanvas()
|
||||
reject(new Error('ctx error'))
|
||||
return false
|
||||
}
|
||||
const canvas = canvasNode.current
|
||||
Taro.getImageInfo({
|
||||
src: getCDNSource('/user/inviteCodePopup.png'),
|
||||
success: (res) => {
|
||||
canvas.width = res.width / 2
|
||||
canvas.height = res.height / 2
|
||||
getImageObject(canvas, `${res.path}`).then((image) => {
|
||||
// 开始绘制
|
||||
startPaint(ctx.current, canvasNode.current, image)
|
||||
resolve(true)
|
||||
}).catch((error) => {
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
// 加载更多
|
||||
const handleLoadMore = () => {
|
||||
|
||||
}
|
||||
const handleQRcodeShare = async() => {
|
||||
await drawPictorial()
|
||||
setShowPopup(true)
|
||||
}
|
||||
// 复制二维码
|
||||
const handleCopyInviteCode = () => {
|
||||
Taro.setClipboardData({
|
||||
data: 'HWWG',
|
||||
})
|
||||
}
|
||||
const handleChange = (value: boolean) => {
|
||||
console.log('value', value)
|
||||
|
||||
setShowPopup(value)
|
||||
}
|
||||
useReady(() => {
|
||||
getInviteCode()
|
||||
setTimeout(() => {
|
||||
initCanvas()
|
||||
}, 200)
|
||||
})
|
||||
|
||||
return <View className={style.main}>
|
||||
<View className={style.content}>
|
||||
<View className={style.background}>
|
||||
<View className={style.left}>
|
||||
<View className={style.title}>蜘蛛管家</View>
|
||||
<View className={style.description}>真挚邀请您建立合作关系</View>
|
||||
</View>
|
||||
<View className={style.right}>
|
||||
<View className={style.iconContainer}>
|
||||
<Image className={style.icon} src={getCDNSource('/user/inviteCode.png')} mode="widthFix" />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<LayoutBlock circle customStyle={{ paddingTop: '10px', paddingBottom: '10px' }}>
|
||||
<View className={style.codeBar}>
|
||||
<View className={style.inviteCodeBar}>
|
||||
<View className={style.invite}>邀请码</View>
|
||||
<Divider direction="vertical" />
|
||||
<View className={style.invite}>{inviteInfo.invitation_code}</View>
|
||||
</View>
|
||||
<View className={style.tips}>填写邀请码,即可在蜘蛛管家下单购物</View>
|
||||
</View>
|
||||
</LayoutBlock>
|
||||
<LayoutBlock circle customStyle={{ paddingTop: '10px', paddingBottom: '10px' }}>
|
||||
<View className={style.inviteListTitle}>
|
||||
<View className={style.titleIconLeft}></View>
|
||||
<Text className={style.listTitle}>成功邀请</Text>
|
||||
<View className={style.titleIconRight}></View>
|
||||
</View>
|
||||
<View className={style.inviteList}>
|
||||
<Table columns={currentTable.columns} emptyText="暂无邀请信息" safeAreaInsetBottom={false} dataSource={currentTable.dataSource} onLoadMore={handleLoadMore}></Table>
|
||||
</View>
|
||||
</LayoutBlock>
|
||||
<View className={style.tips} style={{ justifyContent: 'flex-start' }}>温馨提示:邀请码确定绑定后,不支持解绑。</View>
|
||||
</View>
|
||||
<Canvas style="position: absolute; left: -9999rpx" id="canvas" canvas-id="canvas" type="2d" />
|
||||
<View className={style.bottomBar}>
|
||||
<NormalButton plain type="primary" customStyles={{ width: '45%' }} round onClick={handleQRcodeShare}>
|
||||
二维码分享
|
||||
</NormalButton>
|
||||
<NormalButton type="primary" round customStyles={{ width: '45%' }} onClick={handleCopyInviteCode}>
|
||||
复制邀请码
|
||||
</NormalButton>
|
||||
</View>
|
||||
<Dialog show={showPopup} onChange={handleChange}>
|
||||
<View className={style.codePreview}>
|
||||
<View className={style.imageContainer}>
|
||||
{/* showMenuByLongpress 属性只对 小程序有效 */}
|
||||
<Image className={style.image} src={targetImageUrl} mode="widthFix" id="originImage" showMenuByLongpress />
|
||||
</View>
|
||||
<Text className={style.previewTips}>长按图片保存到手机</Text>
|
||||
</View>
|
||||
</Dialog>
|
||||
|
||||
</View>
|
||||
}
|
||||
export default InviteCode
|
||||
Reference in New Issue
Block a user