【【电子商城】11-21(UI还原-1)】 https://www.tapd.cn/53459131/bugtrace/bugs/view/1153459131001000961
337 lines
12 KiB
TypeScript
337 lines
12 KiB
TypeScript
import { Canvas, Image, Text, View } from '@tarojs/components'
|
||
import Taro, { useReady } from '@tarojs/taro'
|
||
import { useRef, useState } from 'react'
|
||
import styles from './index.module.scss'
|
||
import useLogin from '@/use/useLogin'
|
||
import { alert, goLink } from '@/common/common'
|
||
import { GenBarCodeOrQrCode, GetInvitationInfo, GetInvitationInfoApi, GetInviteCodeList, GetInviteeRecord } from '@/api/user'
|
||
import { getFilterData } from '@/common/util'
|
||
import LayoutBlock from '@/components/layoutBlock'
|
||
import { getCDNSource } from '@/common/constant'
|
||
import NormalButton from '@/components/normalButton'
|
||
import Dialog from '@/components/Dialog'
|
||
import IconText from '@/components/iconText'
|
||
import BindSalesManDialog from '@/components/bindSalesManDialog'
|
||
// 获取业务员信息
|
||
interface Param { inviter_id: number; inviter_name: string; phone: string }
|
||
|
||
const BindSalesman = () => {
|
||
useLogin()
|
||
|
||
const [inviteInfo, setInviteInfo] = useState<any>({})
|
||
|
||
const [salesMan, setSalesMan] = useState<Param | null>(null)
|
||
// const { fetchData: GetInvitationInfoFetchData } = GetInvitationInfoApi()
|
||
// const getInvitationInfo = async() => {
|
||
// const res = await GetInvitationInfoFetchData(getFilterData({ ...inviteInfo }))
|
||
// res.success ? setSalesMan(res.data) : setSalesMan(null)
|
||
// }
|
||
// 获取被邀请记录
|
||
const { fetchData: getInviteeRecordAPI } = GetInviteeRecord()
|
||
const getInviteeRecord = async() => {
|
||
const res = await getInviteeRecordAPI()
|
||
if (res.success) {
|
||
setSalesMan(res.data)
|
||
}
|
||
}
|
||
const { fetchData } = GetInvitationInfo()
|
||
// 获取邀请码
|
||
const getInviteCode = async() => {
|
||
const res = await fetchData()
|
||
if (res.success) {
|
||
setInviteInfo(res.data)
|
||
// getInvitationInfo()
|
||
}
|
||
}
|
||
const [invite, setInvite] = useState<number>(0)
|
||
const { fetchData: getInvitationListAPI } = GetInviteCodeList()
|
||
const getInvitationList = async() => {
|
||
const res = await getInvitationListAPI()
|
||
if (res.success) {
|
||
console.log('getInviteCode', res)
|
||
setInvite(res.data.total)
|
||
}
|
||
}
|
||
|
||
const canvasNode = useRef<Taro.Canvas | null>(null)
|
||
const ctx = useRef<Taro.RenderingContext | null>(null)
|
||
const [showPopup, setShowPopup] = useState(false)
|
||
|
||
const [loading, setLoading] = useState(false)
|
||
const { fetchData: genCode } = GenBarCodeOrQrCode()
|
||
// 生成二维码
|
||
const genQRcode = async() => {
|
||
const res = await genCode({ content: `InviteCode:${inviteInfo.invitation_code}` })
|
||
if (res.success) {
|
||
return res.data.qrcode_base64
|
||
}
|
||
else {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
const inviteDialog = useRef<any>(null)
|
||
|
||
const getImageObject = (canvas: Taro.Canvas, src: string) => {
|
||
return new Promise<Taro.Image>((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)
|
||
}
|
||
})
|
||
}
|
||
const doublePick = (num, minus = false) => {
|
||
if (minus) {
|
||
return num / 2
|
||
}
|
||
else {
|
||
return num * 2
|
||
}
|
||
}
|
||
const [targetImageUrl, setTargetImageUrl] = useState('')
|
||
// canvas 生成 图片
|
||
const saveCanvasToImage = (canvas) => {
|
||
Taro.canvasToTempFilePath({
|
||
canvas,
|
||
fileType: 'png',
|
||
success: (res) => {
|
||
console.log('tempFilePath', res.tempFilePath)
|
||
setTargetImageUrl(res.tempFilePath)
|
||
},
|
||
fail: (error) => {
|
||
console.log('error', error)
|
||
},
|
||
complete: () => {
|
||
setLoading(false)
|
||
},
|
||
})
|
||
}
|
||
const [, setForceUpdate] = useState({})
|
||
// 初始化 canvas
|
||
const initCanvas = async() => {
|
||
return new Promise((resolve, reject) => {
|
||
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({})
|
||
resolve(true)
|
||
}).exec()
|
||
})
|
||
})
|
||
}
|
||
const [canvasStyle, setCanvasStyle] = useState<React.CSSProperties>({})
|
||
const startPaint = async(ctx: Taro.RenderingContext, canvas: Taro.Canvas, image: Taro.Image) => {
|
||
// 开始绘制
|
||
const { width, height } = canvas
|
||
console.log('startPaint param', ctx, canvas, image)
|
||
const cCanvasCtx = ctx as any
|
||
cCanvasCtx.clearRect(0, 0, width, height)
|
||
cCanvasCtx.drawImage(image, 0, 0, width, height)
|
||
cCanvasCtx.save()
|
||
cCanvasCtx.font = `${doublePick(40)}px 微软雅黑`
|
||
cCanvasCtx.fillStyle = '#000000'
|
||
cCanvasCtx.fillText('陆盈商城', doublePick(40), doublePick(80)) // text up above canvas
|
||
cCanvasCtx.save()
|
||
cCanvasCtx.font = `${doublePick(26)}px 微软雅黑`
|
||
cCanvasCtx.fillStyle = '#8f9398'
|
||
cCanvasCtx.fillText('邀请好友加入', doublePick(40), doublePick(130)) // text up above canvas
|
||
cCanvasCtx.save()
|
||
cCanvasCtx.font = `${doublePick(24)}px 微软雅黑`
|
||
cCanvasCtx.fillStyle = '#a6a6a6'
|
||
cCanvasCtx.fillText('请前往陆盈商城,进行填写或扫描邀请', doublePick(100), doublePick(630)) // text up above canvas
|
||
cCanvasCtx.save()
|
||
cCanvasCtx.font = `${doublePick(36)}px 微软雅黑`
|
||
cCanvasCtx.fillStyle = '#7f7f7f'
|
||
cCanvasCtx.fillText('邀 请 码', doublePick(72), doublePick(730)) // text up above canvas
|
||
cCanvasCtx.save()
|
||
cCanvasCtx.font = `${doublePick(24)}px 微软雅黑`
|
||
cCanvasCtx.fillStyle = '#cccccc'
|
||
cCanvasCtx.fillText('|', doublePick(258), doublePick(724)) // text up above canvas
|
||
cCanvasCtx.save()
|
||
cCanvasCtx.font = `${doublePick(36)}px 微软雅黑`
|
||
cCanvasCtx.fillStyle = '#7f7f7f'
|
||
cCanvasCtx.fillText(`${inviteInfo.invitation_code}`, doublePick(311), doublePick(730)) // text up above canvas
|
||
cCanvasCtx.save()
|
||
const codeUrl = await genQRcode()
|
||
try {
|
||
const code = await getImageObject(canvas, codeUrl)
|
||
cCanvasCtx.drawImage(code, doublePick(110), doublePick(213), doublePick(342), doublePick(342))
|
||
}
|
||
catch (err) {
|
||
console.error('合成二维邀请码失败', err)
|
||
setLoading(false)
|
||
throw new Error('合成二维邀请码失败')
|
||
}
|
||
|
||
saveCanvasToImage(canvas)
|
||
}
|
||
// 绘制最终的海报,图片使用两倍大小,canvas大小是图片的二分之一 就能让canvas生成出来的图片清晰了
|
||
const drawPictorial = async() => {
|
||
// eslint-disable-next-line no-async-promise-executor
|
||
return new Promise(async(resolve, reject) => {
|
||
setLoading(true)
|
||
if (!ctx.current) {
|
||
// 重新初始化canvas
|
||
await initCanvas()
|
||
}
|
||
const canvas = canvasNode.current!
|
||
Taro.getImageInfo({
|
||
src: getCDNSource('/user/inviteCodePopup.png'),
|
||
success: (res) => {
|
||
console.log('res==>', res)
|
||
canvas.width = res.width
|
||
canvas.height = res.height
|
||
setCanvasStyle({
|
||
width: `${doublePick(canvas.width, true)}px`,
|
||
height: `${doublePick(canvas.height, true)}px`,
|
||
})
|
||
getImageObject(canvas, `${res.path}`).then(async(image) => {
|
||
try {
|
||
// 开始绘制
|
||
await startPaint(ctx.current!, canvas, image)
|
||
resolve(true)
|
||
}
|
||
catch (err) {
|
||
console.log(err)
|
||
setLoading(false)
|
||
reject(new Error('绘制失败'))
|
||
}
|
||
}).catch((error) => {
|
||
setLoading(false)
|
||
throw new Error(error)
|
||
})
|
||
},
|
||
})
|
||
})
|
||
}
|
||
// 复制二维码
|
||
const handleCopyInviteCode = () => {
|
||
Taro.setClipboardData({
|
||
data: inviteInfo.invitation_code,
|
||
})
|
||
}
|
||
const handleChange = (value: boolean) => {
|
||
setShowPopup(value)
|
||
}
|
||
useReady(() => {
|
||
getInviteCode()
|
||
getInvitationList()
|
||
getInviteeRecord()
|
||
setTimeout(() => {
|
||
initCanvas()
|
||
}, 200)
|
||
})
|
||
const handleQRcodeShare = async() => {
|
||
try {
|
||
const flag = await drawPictorial()
|
||
if (flag) {
|
||
setShowPopup(true)
|
||
}
|
||
}
|
||
catch (err) {
|
||
throw new Error('弹出二维码失败')
|
||
}
|
||
}
|
||
// 邀请记录
|
||
const handleInviteCord = () => {
|
||
goLink('/pages/inviteCode/inviteCord/index')
|
||
}
|
||
const handleBindSalesMan = () => {
|
||
inviteDialog.current.handleChange(true)
|
||
}
|
||
const handleBindSuccess = () => {
|
||
getInviteeRecord()
|
||
}
|
||
return (
|
||
<View className={styles.main}>
|
||
<View className={styles.content}>
|
||
<View className={styles.background}>
|
||
<View className={styles.left}>
|
||
<View className={styles.title}>陆盈商城</View>
|
||
<View className={styles.description}>邀请好友加入</View>
|
||
</View>
|
||
<View className={styles.right}>
|
||
<View className={styles.iconContainer}>
|
||
<Image className={styles.icon} src={getCDNSource('/user/inviteCode.png')} mode="widthFix" />
|
||
</View>
|
||
</View>
|
||
</View>
|
||
<View className={styles.inviteCodeContent}>
|
||
<LayoutBlock circle>
|
||
<View className={styles.codeBar}>
|
||
<View className={styles.inviteCodeBar}>
|
||
<View className={styles.invite}>{inviteInfo.invitation_code ? inviteInfo.invitation_code : '暂无邀请码'}</View>
|
||
</View>
|
||
<View className={styles.codeTitle}>您的专属邀请码</View>
|
||
</View>
|
||
</LayoutBlock>
|
||
<LayoutBlock circle>
|
||
<View className={styles.inviteCord}>
|
||
<View className={styles.inviteCordTitle}>
|
||
<Text className={styles.titleText}>我的邀请记录:</Text>
|
||
{invite === 0 && <Text className={styles.noop}>暂无邀请信息</Text>}
|
||
</View>
|
||
{invite !== 0 && <Text>{invite}人</Text>}
|
||
<View className={styles.inviteCordMore} onClick={handleInviteCord}>
|
||
<IconText svg text="查看" direction="right" iconName="icon-rukou" textCustomStyle={{ color: '#337FFF' }} color="#337FFF"></IconText>
|
||
</View>
|
||
</View>
|
||
</LayoutBlock>
|
||
<LayoutBlock circle>
|
||
<View className={styles.inviteCord}>
|
||
<View className={styles.inviteCordTitle}>
|
||
<Text className={styles.titleText}>朋友邀请我:</Text>
|
||
{!salesMan?.inviter_id && <Text className={styles.noop}>未绑定</Text>}
|
||
</View>
|
||
{!!salesMan?.inviter_id && <View className={styles.inviteCordMore}>
|
||
已绑定 {salesMan.inviter_name}({salesMan.phone})
|
||
</View>}
|
||
{!salesMan?.inviter_id
|
||
&& <View onClick={handleBindSalesMan}>
|
||
<IconText svg text="立即绑定" direction="right" iconName="icon-rukou" textCustomStyle={{ color: '#337FFF' }} color="#337FFF"></IconText>
|
||
</View>
|
||
}
|
||
</View>
|
||
</LayoutBlock>
|
||
<View className={styles.tips} style={{ justifyContent: 'flex-start' }}>温馨提示:邀请码确定绑定后,不支持解绑。</View>
|
||
</View>
|
||
</View>
|
||
{/* 已踩坑,这里必须设置canvas的style的width和height,单单只设置canvas实例的width和height是不行的。会模糊! */}
|
||
<Canvas style={{ position: 'absolute', left: '-9999rpx', ...canvasStyle }} id="canvas" type="2d" />
|
||
<View className={styles.bottomBar}>
|
||
<NormalButton loading={loading} plain type="primary" customTextClassName={styles.bottomBar__text} customStyles={{ width: '45%' }} round onClick={handleQRcodeShare}>
|
||
二维码分享
|
||
</NormalButton>
|
||
<NormalButton type="primary" round customTextClassName={styles.bottomBar__text} customStyles={{ width: '45%' }} onClick={handleCopyInviteCode}>
|
||
复制邀请码
|
||
</NormalButton>
|
||
</View>
|
||
<Dialog show={showPopup} onChange={handleChange}>
|
||
<View className={styles.codePreview}>
|
||
<View className={styles.imageContainer}>
|
||
{/* showMenuByLongpress 属性只对 小程序有效 */}
|
||
<Image className={styles.image} src={targetImageUrl} mode="widthFix" id="originImage" showMenuByLongpress />
|
||
</View>
|
||
<Text className={styles.previewTips}>长按图片保存到手机</Text>
|
||
</View>
|
||
</Dialog>
|
||
<BindSalesManDialog ref={inviteDialog} onSuccess={handleBindSuccess} />
|
||
</View>
|
||
)
|
||
}
|
||
|
||
export default BindSalesman
|