邀请页面
This commit is contained in:
@@ -116,6 +116,14 @@ export default {
|
|||||||
'draw.pwdPrompt': '请输入资金密码',
|
'draw.pwdPrompt': '请输入资金密码',
|
||||||
'draw.success': '您的提币请求正在处理中!经过区块链网络确认后即可到账,请注意查收。',
|
'draw.success': '您的提币请求正在处理中!经过区块链网络确认后即可到账,请注意查收。',
|
||||||
|
|
||||||
|
'invite.title': '邀请新人',
|
||||||
|
'invite.title2': '推荐新人领奖励',
|
||||||
|
'invite.title3': '我的专属邀请码和链接',
|
||||||
|
'invite.btn': '转发邀请到其他应用',
|
||||||
|
'invite.step1': '分享好友',
|
||||||
|
'invite.step2': '注册',
|
||||||
|
'invite.step3': '领取奖金',
|
||||||
|
|
||||||
'profile.account': 'Account Settings',
|
'profile.account': 'Account Settings',
|
||||||
'profile.system': 'System Settings',
|
'profile.system': 'System Settings',
|
||||||
'profile.identity': '实名认证',
|
'profile.identity': '实名认证',
|
||||||
|
|||||||
@@ -116,6 +116,14 @@ export default {
|
|||||||
'draw.pwdPrompt': '请输入资金密码',
|
'draw.pwdPrompt': '请输入资金密码',
|
||||||
'draw.success': '您的提币请求正在处理中!经过区块链网络确认后即可到账,请注意查收。',
|
'draw.success': '您的提币请求正在处理中!经过区块链网络确认后即可到账,请注意查收。',
|
||||||
|
|
||||||
|
'invite.title': '邀请新人',
|
||||||
|
'invite.title2': '推荐新人领奖励',
|
||||||
|
'invite.title3': '我的专属邀请码和链接',
|
||||||
|
'invite.btn': '转发邀请到其他应用',
|
||||||
|
'invite.step1': '分享好友',
|
||||||
|
'invite.step2': '注册',
|
||||||
|
'invite.step3': '领取奖金',
|
||||||
|
|
||||||
'profile.account': '账户管理',
|
'profile.account': '账户管理',
|
||||||
'profile.system': '系统设置',
|
'profile.system': '系统设置',
|
||||||
'profile.identity': '实名认证',
|
'profile.identity': '实名认证',
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ const model: Model = {
|
|||||||
coin,
|
coin,
|
||||||
basic,
|
basic,
|
||||||
balance: retrieveBalance(basic, coin),
|
balance: retrieveBalance(basic, coin),
|
||||||
|
address: '',
|
||||||
|
amount: '',
|
||||||
|
password: '',
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
yield put({
|
yield put({
|
||||||
@@ -140,7 +143,7 @@ const model: Model = {
|
|||||||
type: 'onUpdateState',
|
type: 'onUpdateState',
|
||||||
confirmVisible: false,
|
confirmVisible: false,
|
||||||
});
|
});
|
||||||
Modal.info({
|
Modal.success({
|
||||||
title: formatMessage({ id: 'common.prompt' }),
|
title: formatMessage({ id: 'common.prompt' }),
|
||||||
content: formatMessage({ id: 'draw.success' }),
|
content: formatMessage({ id: 'draw.success' }),
|
||||||
okText: formatMessage({ id: 'common.ok' }),
|
okText: formatMessage({ id: 'common.ok' }),
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ const CommunityPage: React.FC<HomeCommunityModelState & DispatchProp> = ({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className="right app-btn"
|
className="right app-btn"
|
||||||
|
onClick={() => dispatch(routerRedux.push('/invite'))}
|
||||||
>
|
>
|
||||||
{formatMessage({ id: 'home.community.invite' })}
|
{formatMessage({ id: 'home.community.invite' })}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
74
src/pages/invite/index.tsx
Normal file
74
src/pages/invite/index.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* Title: 邀请新人
|
||||||
|
* Routes:
|
||||||
|
* - ./src/pages/routes
|
||||||
|
*/
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { connect, DispatchProp } from 'dva';
|
||||||
|
import { Icon, Button, message } from 'antd';
|
||||||
|
import { formatMessage } from 'umi-plugin-locale';
|
||||||
|
import { QRCanvas } from 'qrcanvas-react';
|
||||||
|
import Clipboard from 'react-clipboard.js';
|
||||||
|
import { InviteModelState } from './model';
|
||||||
|
import Header from '@/components/Header';
|
||||||
|
|
||||||
|
import styles from './style.less';
|
||||||
|
import { UserModelState } from '@/models/user';
|
||||||
|
import { generateInviteHref } from './service';
|
||||||
|
|
||||||
|
const InvitePage: React.FC<DispatchProp & InviteModelState & UserModelState> = ({
|
||||||
|
dispatch,
|
||||||
|
user,
|
||||||
|
}) => {
|
||||||
|
if (!user) return null;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch({ type: 'invite/load' });
|
||||||
|
}, []);
|
||||||
|
const href = generateInviteHref(user.inviteCode);
|
||||||
|
const onAddressCopied = () => {
|
||||||
|
message.success(formatMessage({ id: 'common.copied' }));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<Header
|
||||||
|
className="header"
|
||||||
|
title={formatMessage({ id: 'invite.title' })}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className={styles.body}>
|
||||||
|
<span className={styles.title2}>{formatMessage({ id: 'invite.title2' })}</span>
|
||||||
|
<div className={styles.card}>
|
||||||
|
<div className="rip" />
|
||||||
|
<div className="content">
|
||||||
|
<span className="title3">{formatMessage({ id: 'invite.title3' })}</span>
|
||||||
|
<span className="invite-code">{user.inviteCode}</span>
|
||||||
|
<QRCanvas options={{
|
||||||
|
data: href,
|
||||||
|
size: 200,
|
||||||
|
}} />
|
||||||
|
<div className="href-container">
|
||||||
|
<span>{href}</span>
|
||||||
|
<Clipboard
|
||||||
|
component="div"
|
||||||
|
className="address"
|
||||||
|
data-clipboard-text={href}
|
||||||
|
onSuccess={onAddressCopied}
|
||||||
|
>
|
||||||
|
<Icon type="copy" theme="filled" />
|
||||||
|
</Clipboard>
|
||||||
|
</div>
|
||||||
|
<Button type="primary">
|
||||||
|
{formatMessage({ id: 'invite.btn' })}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
({ invite, user }: any) => ({ ...invite, ...user }),
|
||||||
|
)(InvitePage);
|
||||||
28
src/pages/invite/model.ts
Normal file
28
src/pages/invite/model.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Model, routerRedux } from "dva";
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export interface InviteModelState {
|
||||||
|
}
|
||||||
|
|
||||||
|
const initState: InviteModelState = {
|
||||||
|
};
|
||||||
|
|
||||||
|
const model: Model = {
|
||||||
|
namespace: 'invite',
|
||||||
|
|
||||||
|
state: initState,
|
||||||
|
|
||||||
|
effects: {
|
||||||
|
},
|
||||||
|
|
||||||
|
reducers: {
|
||||||
|
onUpdateState(state, payload) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default model;
|
||||||
18
src/pages/invite/service.ts
Normal file
18
src/pages/invite/service.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
const isLocalhost = Boolean(
|
||||||
|
window.location.hostname === 'localhost' ||
|
||||||
|
// [::1] is the IPv6 localhost address.
|
||||||
|
window.location.hostname === '[::1]' ||
|
||||||
|
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||||
|
window.location.hostname.match(
|
||||||
|
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export function generateInviteHref(invite: string): string {
|
||||||
|
const postfix = invite ? `/check?inviteCode=${invite}` : '/check';
|
||||||
|
if (isLocalhost) {
|
||||||
|
return `${window.location.origin}${postfix}`
|
||||||
|
}
|
||||||
|
return `https://fivapp.github.io/#${postfix}`;
|
||||||
|
}
|
||||||
147
src/pages/invite/style.less
Normal file
147
src/pages/invite/style.less
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
|
||||||
|
@import '../../global.less';
|
||||||
|
|
||||||
|
.container {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(to bottom, @orange-one, @orange-two);
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.header {
|
||||||
|
background-color: rgba(255,255,255,0);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn-primary {
|
||||||
|
border-width: 0;
|
||||||
|
background-color: @orange-one;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title2 {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
@height: 450px;
|
||||||
|
@topHeight: @height * 0.4;
|
||||||
|
@ripSize: 32px;
|
||||||
|
@halfRip: @ripSize / 2;
|
||||||
|
@bottomHeight: @height - @topHeight - @ripSize;
|
||||||
|
@shadowAttr: 1px 1px 8px rgba(0, 0, 0, .2);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
margin: 20px 30px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
height: @height;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
padding: 20px 32px ;
|
||||||
|
|
||||||
|
&::before, &::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
box-shadow: @shadowAttr;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
top: 0;
|
||||||
|
height: @topHeight;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
bottom: 0;
|
||||||
|
height: @bottomHeight;
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.rip {
|
||||||
|
position: absolute;
|
||||||
|
top: @topHeight;
|
||||||
|
left: @halfRip;
|
||||||
|
right: @halfRip;
|
||||||
|
height: @ripSize;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
&::before, &::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: @ripSize / sqrt(2);
|
||||||
|
height: @ripSize / sqrt(2);
|
||||||
|
top: 50%;
|
||||||
|
border: @halfRip solid transparent;
|
||||||
|
border-radius: 100%;
|
||||||
|
border-top-color: #fff;
|
||||||
|
border-right-color: #fff;
|
||||||
|
box-sizing: content-box;
|
||||||
|
box-shadow: inset -2px 2px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: -(sqrt(2) / 4 + 1) * @ripSize;
|
||||||
|
transform: translateY(-50%) rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
right: -(sqrt(2) / 4 + 1) * @ripSize;
|
||||||
|
transform: translateY(-50%) rotate(225deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
z-index: 3;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.title3 {
|
||||||
|
color: @orange-two;
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-code {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.href-container {
|
||||||
|
margin: 20px 0;
|
||||||
|
word-break: break-all;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
margin-left: 8px;
|
||||||
|
color: @orange-one;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn {
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ export interface User {
|
|||||||
phone: string;
|
phone: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
token: string;
|
token: string;
|
||||||
|
inviteCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AreaCode {
|
export interface AreaCode {
|
||||||
|
|||||||
Reference in New Issue
Block a user