✨ feat(邀请码): 完成邀请码模块
This commit is contained in:
parent
3ff32ee209
commit
7d3f6e22c5
@ -9,6 +9,13 @@
|
|||||||
"condition": {
|
"condition": {
|
||||||
"miniprogram": {
|
"miniprogram": {
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"name": "邀请码",
|
||||||
|
"pathName": "/pages/inviteCode/index",
|
||||||
|
"query": "",
|
||||||
|
"launchMode": "default",
|
||||||
|
"scene": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "销售统计",
|
"name": "销售统计",
|
||||||
"pathName": "pages/saleStatistic/index",
|
"pathName": "pages/saleStatistic/index",
|
||||||
|
@ -35,8 +35,6 @@ export {
|
|||||||
TakeGoodsOrderRefuse,
|
TakeGoodsOrderRefuse,
|
||||||
TakeGoodsOrderAudit,
|
TakeGoodsOrderAudit,
|
||||||
TakeGoodsOrder,
|
TakeGoodsOrder,
|
||||||
UserInvitationInfoRecord,
|
|
||||||
GenBarCodeOrQrCode,
|
|
||||||
} from './takeDelivery/index'
|
} from './takeDelivery/index'
|
||||||
|
|
||||||
// 关于销售统计
|
// 关于销售统计
|
||||||
@ -50,6 +48,11 @@ export {
|
|||||||
SaleOrderDataFormdataFormStatus,
|
SaleOrderDataFormdataFormStatus,
|
||||||
SaleOrderDataFormApi,
|
SaleOrderDataFormApi,
|
||||||
} from './statistic/index'
|
} from './statistic/index'
|
||||||
|
// 邀请码
|
||||||
|
export {
|
||||||
|
GetInvitationInfo,
|
||||||
|
GenBarCodeOrQrCode,
|
||||||
|
} from './inviteCode/index'
|
||||||
/**
|
/**
|
||||||
* 系列列表
|
* 系列列表
|
||||||
* @returns
|
* @returns
|
||||||
|
18
src/api/inviteCode/index.ts
Normal file
18
src/api/inviteCode/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { CAP_HTML_TO_IMAGE_BASE_URL } from '@/common/constant'
|
||||||
|
import { useRequest } from '@/use/useHttp'
|
||||||
|
|
||||||
|
// 邀请码
|
||||||
|
export const GetInvitationInfo = () => {
|
||||||
|
return useRequest({
|
||||||
|
url: '/v1/mp/user/invitationInfoRecord',
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 生成二维码
|
||||||
|
export const GenBarCodeOrQrCode = () => {
|
||||||
|
return useRequest({
|
||||||
|
url: '/xima-caphtml/genBarcodeOrQrCode',
|
||||||
|
base_url: CAP_HTML_TO_IMAGE_BASE_URL,
|
||||||
|
method: 'post',
|
||||||
|
})
|
||||||
|
}
|
@ -1,2 +1,2 @@
|
|||||||
export { EnumTakeGoodsOrderStatus, EnumTakeGoodsOrderTypeList } from './enum'
|
export { EnumTakeGoodsOrderStatus, EnumTakeGoodsOrderTypeList } from './enum'
|
||||||
export { TakeGoodsOrderList, TakeGoodsOrderRefuse, TakeGoodsOrderAudit, TakeGoodsOrder, UserInvitationInfoRecord, GenBarCodeOrQrCode } from './takeDelivery'
|
export { TakeGoodsOrderList, TakeGoodsOrderRefuse, TakeGoodsOrderAudit, TakeGoodsOrder } from './takeDelivery'
|
||||||
|
@ -28,18 +28,3 @@ export const TakeGoodsOrder = () => {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 邀请码
|
|
||||||
export const UserInvitationInfoRecord = () => {
|
|
||||||
return useRequest({
|
|
||||||
url: '/v2/mp/user/invitationInfoRecord',
|
|
||||||
method: 'get',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 生成二维码
|
|
||||||
export const GenBarCodeOrQrCode = () => {
|
|
||||||
return useRequest({
|
|
||||||
url: '/xima-caphtml/genBarcodeOrQrCode',
|
|
||||||
base_url: CAP_HTML_TO_IMAGE_BASE_URL,
|
|
||||||
method: 'post',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -36,6 +36,10 @@ export default defineAppConfig({
|
|||||||
'custom-wrapper': '/custom-wrapper',
|
'custom-wrapper': '/custom-wrapper',
|
||||||
},
|
},
|
||||||
subPackages: [
|
subPackages: [
|
||||||
|
{
|
||||||
|
root: 'pages/inviteCode',
|
||||||
|
pages: ['index'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
root: 'pages/saleStatistic',
|
root: 'pages/saleStatistic',
|
||||||
pages: ['index'],
|
pages: ['index'],
|
||||||
|
@ -39,7 +39,10 @@ export const LIST_EMPTY_IMAGE = `${IMG_CND_Prefix}/list_empty.png`
|
|||||||
export const EMPTY_IMAGE = `${IMG_CND_Prefix}/empty.png`
|
export const EMPTY_IMAGE = `${IMG_CND_Prefix}/empty.png`
|
||||||
|
|
||||||
export const SEARCH_EMPTY_IMAGE = `${IMG_CND_Prefix}/search_empty.png`
|
export const SEARCH_EMPTY_IMAGE = `${IMG_CND_Prefix}/search_empty.png`
|
||||||
|
// 获取CND资源
|
||||||
|
export const getCDNSource = (suffix: string) => {
|
||||||
|
return IMG_CND_Prefix + suffix
|
||||||
|
}
|
||||||
// 场景值
|
// 场景值
|
||||||
export const SCENE = {
|
export const SCENE = {
|
||||||
SearchScene: 0, // 商城面料搜索
|
SearchScene: 0, // 商城面料搜索
|
||||||
|
40
src/components/Dialog/index.module.scss
Normal file
40
src/components/Dialog/index.module.scss
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
$am-ms: 200ms;
|
||||||
|
|
||||||
|
.dialog{
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
&_mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity $am-ms ease-in;
|
||||||
|
&_active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
&--hidden {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&_content {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity $am-ms ease-in;
|
||||||
|
&_active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
src/components/Dialog/index.tsx
Normal file
63
src/components/Dialog/index.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { Image, View } from '@tarojs/components'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import style from './index.module.scss'
|
||||||
|
import { usePropsValue } from '@/use/useCommon'
|
||||||
|
|
||||||
|
interface PropsType {
|
||||||
|
showOverLay?: boolean
|
||||||
|
show: boolean
|
||||||
|
onClose?: (show: boolean) => void
|
||||||
|
onChange?: (isShow) => void
|
||||||
|
children?: React.ReactNode
|
||||||
|
}
|
||||||
|
// 弹出框
|
||||||
|
const Dialog = (props: PropsType) => {
|
||||||
|
const { showOverLay = true, show = false, onClose: _onClose, onChange: _onChange, children } = props
|
||||||
|
|
||||||
|
const [_show, setShow] = usePropsValue({
|
||||||
|
value: show,
|
||||||
|
defaultValue: false,
|
||||||
|
onChange: (value) => {
|
||||||
|
_onChange?.(value)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const [animShow, setAnimShow] = useState(false)
|
||||||
|
|
||||||
|
const handleAnimShow = () => {
|
||||||
|
setShow(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
setAnimShow(true)
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
const handleAnimHide = () => {
|
||||||
|
setAnimShow(false)
|
||||||
|
setTimeout(() => {
|
||||||
|
setShow(false)
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
const onClose = () => {
|
||||||
|
handleAnimHide()
|
||||||
|
_onClose?.(_show)
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
if (show) {
|
||||||
|
handleAnimShow()
|
||||||
|
}
|
||||||
|
}, [show])
|
||||||
|
return _show
|
||||||
|
? <View className={style.dialog}>
|
||||||
|
{/* 遮罩层 start */}
|
||||||
|
<View
|
||||||
|
className={classnames(style.dialog_mask, { [style.dialog_mask_active]: animShow, [style['drawer_mask--hidden']]: !showOverLay })}
|
||||||
|
onClick={onClose}
|
||||||
|
></View>
|
||||||
|
{/* 遮罩层 end */}
|
||||||
|
<View className={classnames(style.dialog_content, { [style.dialog_content_active]: animShow })}>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
: <View></View>
|
||||||
|
}
|
||||||
|
export default Dialog
|
@ -7,3 +7,6 @@
|
|||||||
color: $color_font_three;
|
color: $color_font_three;
|
||||||
padding: 24px 0;
|
padding: 24px 0;
|
||||||
}
|
}
|
||||||
|
.empty{
|
||||||
|
padding: 100px;
|
||||||
|
}
|
||||||
|
@ -4,7 +4,7 @@ import Iconfont from '../iconfont/iconfont'
|
|||||||
import LoadingCard from '../loadingCard/index'
|
import LoadingCard from '../loadingCard/index'
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
|
|
||||||
export type LoadMoreStatus = 'more' | 'loading' | 'noMore'
|
export type LoadMoreStatus = 'more' | 'loading' | 'noMore' | 'empty'
|
||||||
|
|
||||||
interface LoadMoreEvent {
|
interface LoadMoreEvent {
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
@ -15,10 +15,11 @@ interface LoadMorePropsType extends LoadMoreEvent {
|
|||||||
moreText?: string
|
moreText?: string
|
||||||
loadingText?: string
|
loadingText?: string
|
||||||
noMoreText?: string
|
noMoreText?: string
|
||||||
|
emptyText?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const LoadMore: FC<LoadMorePropsType> = (props) => {
|
const LoadMore: FC<LoadMorePropsType> = (props) => {
|
||||||
const { status, moreText = '查看更多', loadingText = '加载中', noMoreText = '没有更多', onClick } = props
|
const { status, moreText = '查看更多', loadingText = '加载中', noMoreText = '没有更多', emptyText = '暂无数据', onClick } = props
|
||||||
|
|
||||||
const handleShowMore = () => {
|
const handleShowMore = () => {
|
||||||
onClick && onClick()
|
onClick && onClick()
|
||||||
@ -36,6 +37,13 @@ const LoadMore: FC<LoadMorePropsType> = (props) => {
|
|||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
else if (status === 'empty') {
|
||||||
|
component = (
|
||||||
|
<View className={styles.empty}>
|
||||||
|
<Text>{emptyText}</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
component = <Text>{noMoreText}</Text>
|
component = <Text>{noMoreText}</Text>
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@ interface Params {
|
|||||||
refresherEnabled?: boolean
|
refresherEnabled?: boolean
|
||||||
enableBackToTop?: boolean
|
enableBackToTop?: boolean
|
||||||
emptySlot?: React.ReactNode
|
emptySlot?: React.ReactNode
|
||||||
|
moreText?: string
|
||||||
|
loadingText?: string
|
||||||
|
noMoreText?: string
|
||||||
}
|
}
|
||||||
const InfiniteScroll = ({
|
const InfiniteScroll = ({
|
||||||
styleObj,
|
styleObj,
|
||||||
|
@ -26,10 +26,15 @@ export interface TablePropsType {
|
|||||||
dataSource?: { list: RecordType[]; total: number }
|
dataSource?: { list: RecordType[]; total: number }
|
||||||
onLoadMore?: () => void
|
onLoadMore?: () => void
|
||||||
height?: number
|
height?: number
|
||||||
|
moreText?: string
|
||||||
|
loadingText?: string
|
||||||
|
noMoreText?: string
|
||||||
|
emptyText?: string
|
||||||
|
safeAreaInsetBottom?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const Table: FC<TablePropsType> = (props) => {
|
const Table: FC<TablePropsType> = (props) => {
|
||||||
const { columns, dataSource, onLoadMore, height = 400 } = props
|
const { columns, dataSource, onLoadMore, height = 400, moreText = '查看更多', loadingText = '加载中', noMoreText = '没有更多了', emptyText = '暂无数据', safeAreaInsetBottom = true } = props
|
||||||
|
|
||||||
const handleShowMore = () => {
|
const handleShowMore = () => {
|
||||||
onLoadMore && onLoadMore()
|
onLoadMore && onLoadMore()
|
||||||
@ -54,15 +59,18 @@ const Table: FC<TablePropsType> = (props) => {
|
|||||||
|
|
||||||
const loadMoreComponent = useMemo(() => {
|
const loadMoreComponent = useMemo(() => {
|
||||||
if (dataSource) {
|
if (dataSource) {
|
||||||
if (dataSource.list.length < dataSource.total) {
|
if (dataSource.list.length === 0 && dataSource.list.length === dataSource.total) {
|
||||||
return <LoadMore status="more" onClick={handleShowMore}></LoadMore>
|
return <LoadMore emptyText={emptyText} status="empty"></LoadMore>
|
||||||
|
}
|
||||||
|
else if (dataSource.list.length < dataSource.total) {
|
||||||
|
return <LoadMore moreText={moreText} status="more" onClick={handleShowMore}></LoadMore>
|
||||||
}
|
}
|
||||||
else if (dataSource.list.length >= dataSource.total) {
|
else if (dataSource.list.length >= dataSource.total) {
|
||||||
return <LoadMore status="noMore" onClick={handleShowMore}></LoadMore>
|
return <LoadMore noMoreText={noMoreText} status="noMore"></LoadMore>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return <LoadMore status="loading" onClick={handleShowMore}></LoadMore>
|
return <LoadMore moreText={moreText} loadingText={loadingText} noMoreText={noMoreText} status="loading"></LoadMore>
|
||||||
}
|
}
|
||||||
}, [dataSource])
|
}, [dataSource])
|
||||||
|
|
||||||
@ -111,7 +119,7 @@ const Table: FC<TablePropsType> = (props) => {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
<InfiniteScroll enableLoadMoreStatus={false} safeAreaInsetBottom>
|
<InfiniteScroll enableLoadMoreStatus={false} safeAreaInsetBottom={safeAreaInsetBottom}>
|
||||||
{sourceContainer()}
|
{sourceContainer()}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</View>
|
</View>
|
||||||
|
7
src/pages/inviteCode/index.config.ts
Normal file
7
src/pages/inviteCode/index.config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export default {
|
||||||
|
navigationBarTitleText: '邀请码',
|
||||||
|
navigationBarTextStyle: 'black',
|
||||||
|
navigationBarBackgroundColor: '#E4EEFD',
|
||||||
|
backgroundColor: '#E4EEFD',
|
||||||
|
backgroundColorTop: '#E4EEFD',
|
||||||
|
}
|
143
src/pages/inviteCode/index.module.scss
Normal file
143
src/pages/inviteCode/index.module.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
275
src/pages/inviteCode/index.tsx
Normal file
275
src/pages/inviteCode/index.tsx
Normal file
@ -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
|
@ -1,9 +1,8 @@
|
|||||||
page {
|
page{
|
||||||
height: 100vh;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
.login {
|
.login {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
background-color: white;
|
background-color: white;
|
||||||
Image{
|
Image{
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
@ -34,7 +33,7 @@ page {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid #c2c2c2;
|
border: 1px solid #c2c2c2;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
margin-top: 40px;
|
margin-top: 4vh;
|
||||||
padding: 15px 20px;
|
padding: 15px 20px;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
@mixin inputBaseStyle {
|
@mixin inputBaseStyle {
|
||||||
@ -64,13 +63,13 @@ page {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row nowrap;
|
flex-flow: row nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 40px;
|
margin-top: 4vh;
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
color: #909090;
|
color: #909090;
|
||||||
}
|
}
|
||||||
&-button {
|
&-button {
|
||||||
height: 90px;
|
height: 90px;
|
||||||
margin-top: 120px;
|
margin-top: 7vh;
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +81,7 @@ page {
|
|||||||
margin: 0 20px;
|
margin: 0 20px;
|
||||||
}
|
}
|
||||||
.quick-login {
|
.quick-login {
|
||||||
margin-top: 100px;
|
margin-top: 6vh;
|
||||||
&--options {
|
&--options {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row nowrap;
|
flex-flow: row nowrap;
|
||||||
|
@ -128,7 +128,8 @@ const Login: FC = () => {
|
|||||||
<NormalButton circle onClick={handleLogin} customClassName={styles['login-button']} disabled={account === '' || password === ''}>
|
<NormalButton circle onClick={handleLogin} customClassName={styles['login-button']} disabled={account === '' || password === ''}>
|
||||||
登录
|
登录
|
||||||
</NormalButton>
|
</NormalButton>
|
||||||
<QuickLoginWithMemo />
|
{/* H5 显示微信登陆 */}
|
||||||
|
{process.env.TARO_ENV === 'h5' && <QuickLoginWithMemo />}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -56,7 +56,7 @@ const feature: IconCardType[] = [
|
|||||||
{
|
{
|
||||||
iconName: 'icon-yaoqingma',
|
iconName: 'icon-yaoqingma',
|
||||||
name: '邀请码',
|
name: '邀请码',
|
||||||
path: '',
|
path: '/pages/inviteCode/index',
|
||||||
jurisdiction: 'invitation_code_page',
|
jurisdiction: 'invitation_code_page',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user