邀请页面

This commit is contained in:
saplf
2019-09-06 14:08:11 +08:00
parent c8f919ce5a
commit f8598d34f7
9 changed files with 289 additions and 1 deletions

View File

@@ -116,6 +116,14 @@ export default {
'draw.pwdPrompt': '请输入资金密码',
'draw.success': '您的提币请求正在处理中!经过区块链网络确认后即可到账,请注意查收。',
'invite.title': '邀请新人',
'invite.title2': '推荐新人领奖励',
'invite.title3': '我的专属邀请码和链接',
'invite.btn': '转发邀请到其他应用',
'invite.step1': '分享好友',
'invite.step2': '注册',
'invite.step3': '领取奖金',
'profile.account': 'Account Settings',
'profile.system': 'System Settings',
'profile.identity': '实名认证',

View File

@@ -116,6 +116,14 @@ export default {
'draw.pwdPrompt': '请输入资金密码',
'draw.success': '您的提币请求正在处理中!经过区块链网络确认后即可到账,请注意查收。',
'invite.title': '邀请新人',
'invite.title2': '推荐新人领奖励',
'invite.title3': '我的专属邀请码和链接',
'invite.btn': '转发邀请到其他应用',
'invite.step1': '分享好友',
'invite.step2': '注册',
'invite.step3': '领取奖金',
'profile.account': '账户管理',
'profile.system': '系统设置',
'profile.identity': '实名认证',

View File

@@ -63,6 +63,9 @@ const model: Model = {
coin,
basic,
balance: retrieveBalance(basic, coin),
address: '',
amount: '',
password: '',
});
} catch (e) {
yield put({
@@ -140,7 +143,7 @@ const model: Model = {
type: 'onUpdateState',
confirmVisible: false,
});
Modal.info({
Modal.success({
title: formatMessage({ id: 'common.prompt' }),
content: formatMessage({ id: 'draw.success' }),
okText: formatMessage({ id: 'common.ok' }),

View File

@@ -65,6 +65,7 @@ const CommunityPage: React.FC<HomeCommunityModelState & DispatchProp> = ({
<div
className="right app-btn"
onClick={() => dispatch(routerRedux.push('/invite'))}
>
{formatMessage({ id: 'home.community.invite' })}
</div>

View 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
View 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;

View 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
View 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;
}
}
}
}

View File

@@ -11,6 +11,7 @@ export interface User {
phone: string;
email?: string;
token: string;
inviteCode: string;
}
export interface AreaCode {