Commit 5e0b5d00 authored by 愚道's avatar 愚道

feat: init blocks for Ant Design Pro V4!

parent bdc92d5c
/yarn.lock
/package-lock.json
/dist
/node_modules
.umi
.umi-production
export default {
plugins: [
['umi-plugin-block-dev', { layout: 'ant-design-pro' }],
[
'umi-plugin-react',
{
dva: true,
locale: true,
antd: true,
},
],
],
};
# @umi-blocks/ant-design-pro/accountcenter
AccountCenter
## Usage
```sh
umi block add ant-design-pro/AccountCenter
```
## SNAPSHOT
![SNAPSHOT](./snapshot.png)
## LICENSE
MIT
{
"name": "@umi-block/account-center",
"version": "0.0.1",
"description": "AccountCenter",
"main": "src/index.js",
"scripts": {
"dev": "umi dev"
},
"repository": {
"type": "git",
"url": "https://github.com/umijs/umi-blocks/ant-design-pro/accountcenter"
},
"dependencies": {
"antd": "^3.10.9",
"dva": "^2.4.0",
"react": "^16.6.3",
"umi-request": "^1.0.0"
},
"devDependencies": {
"umi": "^2.3.0-beta.1",
"umi-plugin-react": "^1.3.0-beta.1",
"umi-plugin-block-dev": "^1.0.0"
},
"license": "ISC"
}
@import '~antd/lib/style/themes/default.less';
@import '~@/utils/utils.less';
.avatarHolder {
text-align: center;
......@@ -80,7 +79,10 @@
margin-bottom: 24px;
color: @text-color;
transition: color 0.3s;
.textOverflow();
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
&:hover {
color: @primary-color;
......
const titles = [
'Alipay',
'Angular',
'Ant Design',
'Ant Design Pro',
'Bootstrap',
'React',
'Vue',
'Webpack',
];
const avatars = [
'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png', // Alipay
'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png', // Angular
'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png', // Ant Design
'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png', // Ant Design Pro
'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png', // Bootstrap
'https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png', // React
'https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png', // Vue
'https://gw.alipayobjects.com/zos/rmsportal/nxkuOJlFJuAUhzlMTCEe.png', // Webpack
];
const getNotice = [
{
id: 'xxx1',
title: titles[0],
logo: avatars[0],
description: '那是一种内在的东西,他们到达不了,也无法触及的',
updatedAt: new Date(),
member: '科学搬砖组',
href: '',
memberLink: '',
},
{
id: 'xxx2',
title: titles[1],
logo: avatars[1],
description: '希望是一个好东西,也许是最好的,好东西是不会消亡的',
updatedAt: new Date('2017-07-24'),
member: '全组都是吴彦祖',
href: '',
memberLink: '',
},
{
id: 'xxx3',
title: titles[2],
logo: avatars[2],
description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
updatedAt: new Date(),
member: '中二少女团',
href: '',
memberLink: '',
},
{
id: 'xxx4',
title: titles[3],
logo: avatars[3],
description: '那时候我只会想自己想要什么,从不想自己拥有什么',
updatedAt: new Date('2017-07-23'),
member: '程序员日常',
href: '',
memberLink: '',
},
{
id: 'xxx5',
title: titles[4],
logo: avatars[4],
description: '凛冬将至',
updatedAt: new Date('2017-07-23'),
member: '高逼格设计天团',
href: '',
memberLink: '',
},
{
id: 'xxx6',
title: titles[5],
logo: avatars[5],
description: '生命就像一盒巧克力,结果往往出人意料',
updatedAt: new Date('2017-07-23'),
member: '骗你来学计算机',
href: '',
memberLink: '',
},
];
export default {
// 支持值为 Object 和 Array
'GET /api/BLOCK_NAME/currentUser': {
name: 'Serati Ma',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
userid: '00000001',
email: 'antdesign@alipay.com',
signature: '海纳百川,有容乃大',
title: '交互专家',
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
notice: getNotice,
tags: [
{
key: '0',
label: '很有想法的',
},
{
key: '1',
label: '专注设计',
},
{
key: '2',
label: '辣~',
},
{
key: '3',
label: '大长腿',
},
{
key: '4',
label: '川妹子',
},
{
key: '5',
label: '海纳百川',
},
],
notifyCount: 12,
unreadCount: 11,
country: 'China',
geographic: {
province: {
label: '浙江省',
key: '330000',
},
city: {
label: '杭州市',
key: '330100',
},
},
address: '西湖区工专路 77 号',
phone: '0752-268888888',
},
};
import React, { PureComponent } from 'react';
import { connect } from 'dva';
import Link from 'umi/link';
import { Card, Row, Col, Icon, Avatar, Tag, Divider, Input } from 'antd';
import styles from './Center.less';
const operationTabList = [
{
key: 'articles',
tab: (
<span>
文章 <span style={{ fontSize: 14 }}>(8)</span>
</span>
),
},
{
key: 'applications',
tab: (
<span>
应用 <span style={{ fontSize: 14 }}>(8)</span>
</span>
),
},
{
key: 'projects',
tab: (
<span>
项目 <span style={{ fontSize: 14 }}>(8)</span>
</span>
),
},
];
@connect(({ loading, BLOCK_NAME_CAMEL_CASE }) => ({
currentUser: BLOCK_NAME_CAMEL_CASE.currentUser,
currentUserLoading: loading.effects['BLOCK_NAME_CAMEL_CASE/fetchCurrent'],
}))
class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
static getDerivedStateFromProps(props, state) {
const { match, location } = props;
const { tabKey } = state;
const urlTabKey = location.pathname.replace(`${match.path}/`, '');
if (urlTabKey && urlTabKey !== '/' && tabKey !== urlTabKey) {
return {
tabKey: urlTabKey,
};
}
return null;
}
state = {
newTags: [],
inputVisible: false,
inputValue: '',
tabKey: 'articles',
};
componentDidMount() {
const { dispatch } = this.props;
dispatch({
type: 'BLOCK_NAME_CAMEL_CASE/fetchCurrent',
});
}
onTabChange = key => {
// If you need to sync state to url
// const { match } = this.props;
// router.push(`${match.url}/${key}`);
this.setState({
tabKey: key,
});
};
showInput = () => {
this.setState({ inputVisible: true }, () => this.input.focus());
};
saveInputRef = input => {
this.input = input;
};
handleInputChange = e => {
this.setState({ inputValue: e.target.value });
};
handleInputConfirm = () => {
const { state } = this;
const { inputValue } = state;
let { newTags } = state;
if (inputValue && newTags.filter(tag => tag.label === inputValue).length === 0) {
newTags = [...newTags, { key: `new-${newTags.length}`, label: inputValue }];
}
this.setState({
newTags,
inputVisible: false,
inputValue: '',
});
};
render() {
const { newTags, inputVisible, inputValue, tabKey } = this.state;
const { currentUser, currentUserLoading, children } = this.props;
const dataLoading = currentUserLoading || !(currentUser && Object.keys(currentUser).length);
return (
<Row gutter={24}>
<Col lg={7} md={24}>
<Card bordered={false} style={{ marginBottom: 24 }} loading={dataLoading}>
{!dataLoading ? (
<div>
<div className={styles.avatarHolder}>
<img alt="" src={currentUser.avatar} />
<div className={styles.name}>{currentUser.name}</div>
<div>{currentUser.signature}</div>
</div>
<div className={styles.detail}>
<p>
<i className={styles.title} />
{currentUser.title}
</p>
<p>
<i className={styles.group} />
{currentUser.group}
</p>
<p>
<i className={styles.address} />
{currentUser.geographic.province.label}
{currentUser.geographic.city.label}
</p>
</div>
<Divider dashed />
<div className={styles.tags}>
<div className={styles.tagsTitle}>标签</div>
{currentUser.tags.concat(newTags).map(item => (
<Tag key={item.key}>{item.label}</Tag>
))}
{inputVisible && (
<Input
ref={this.saveInputRef}
type="text"
size="small"
style={{ width: 78 }}
value={inputValue}
onChange={this.handleInputChange}
onBlur={this.handleInputConfirm}
onPressEnter={this.handleInputConfirm}
/>
)}
{!inputVisible && (
<Tag
onClick={this.showInput}
style={{ background: '#fff', borderStyle: 'dashed' }}
>
<Icon type="plus" />
</Tag>
)}
</div>
<Divider style={{ marginTop: 16 }} dashed />
<div className={styles.team}>
<div className={styles.teamTitle}>团队</div>
<Row gutter={36}>
{currentUser.notice.map(item => (
<Col key={item.id} lg={24} xl={12}>
<Link to={item.href}>
<Avatar size="small" src={item.logo} />
{item.member}
</Link>
</Col>
))}
</Row>
</div>
</div>
) : null}
</Card>
</Col>
<Col lg={17} md={24}>
<Card
className={styles.tabsCard}
bordered={false}
tabList={operationTabList}
activeTabKey={tabKey}
onTabChange={this.onTabChange}
>
{children || tabKey}
</Card>
</Col>
</Row>
);
}
}
export default PAGE_NAME_UPPER_CAMEL_CASE;
import { query as queryUsers, queryCurrent } from '@/services/user';
import { query as queryUsers, queryCurrent } from './service';
export default {
namespace: 'user',
namespace: 'BLOCK_NAME_CAMEL_CASE',
state: {
list: [],
......
import request from '@/utils/request';
import request from 'umi-request';
export async function query() {
return request('/api/users');
return request('/api/BLOCK_NAME/users');
}
export async function queryCurrent() {
return request('/api/currentUser');
return request('/api/BLOCK_NAME/currentUser');
}
/yarn.lock
/package-lock.json
/dist
/node_modules
.umi
.umi-production
export default {
plugins: [
[
'umi-plugin-block-dev',
{
layout: 'ant-design-pro',
},
],
[
'umi-plugin-react',
{
dva: true,
locale: true,
antd: true,
},
],
],
};
# @umi-blocks/ant-design-pro/accountsettings
AccountSettings
## Usage
```sh
umi block add ant-design-pro/AccountSettings
```
## SNAPSHOT
![SNAPSHOT](./snapshot.png)
## LICENSE
MIT
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
{
"name": "@umi-block/account-settings",
"version": "0.0.1",
"description": "AccountSettings",
"main": "src/index.js",
"scripts": {
"dev": "umi dev"
},
"repository": {
"type": "git",
"url": "https://github.com/umijs/umi-blocks/ant-design-pro/accountsettings"
},
"dependencies": {
"antd": "^3.10.9",
"dva": "^2.4.0",
"react": "^16.6.3",
"umi-request": "^1.0.0"
},
"devDependencies": {
"umi": "^2.3.0-beta.1",
"umi-plugin-block-dev": "^1.0.0",
"umi-plugin-react": "^1.3.0-beta.1"
},
"license": "ISC"
}
import city from './geographic/city.json';
import province from './geographic/province.json';
function getProvince(req, res) {
return res.json(province);
}
function getCity(req, res) {
return res.json(city[req.params.province]);
}
// 代码中会兼容本地 service mock 以及部署站点的静态数据
export default {
// 支持值为 Object 和 Array
'GET /api/currentUser': {
'GET /api/BLOCK_NAME/currentUser': {
name: 'Serati Ma',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
userid: '00000001',
......@@ -51,88 +61,6 @@ export default {
address: '西湖区工专路 77 号',
phone: '0752-268888888',
},
// GET POST 可省略
'GET /api/users': [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
],
'POST /api/login/account': (req, res) => {
const { password, userName, type } = req.body;
if (password === 'ant.design' && userName === 'admin') {
res.send({
status: 'ok',
type,
currentAuthority: 'admin',
});
return;
}
if (password === 'ant.design' && userName === 'user') {
res.send({
status: 'ok',
type,
currentAuthority: 'user',
});
return;
}
res.send({
status: 'error',
type,
currentAuthority: 'guest',
});
},
'POST /api/register': (req, res) => {
res.send({ status: 'ok', currentAuthority: 'user' });
},
'GET /api/500': (req, res) => {
res.status(500).send({
timestamp: 1513932555104,
status: 500,
error: 'error',
message: 'error',
path: '/base/category/list',
});
},
'GET /api/404': (req, res) => {
res.status(404).send({
timestamp: 1513932643431,
status: 404,
error: 'Not Found',
message: 'No message available',
path: '/base/category/list/2121212',
});
},
'GET /api/403': (req, res) => {
res.status(403).send({
timestamp: 1513932555104,
status: 403,
error: 'Unauthorized',
message: 'Unauthorized',
path: '/base/category/list',
});
},
'GET /api/401': (req, res) => {
res.status(401).send({
timestamp: 1513932555104,
status: 401,
error: 'Unauthorized',
message: 'Unauthorized',
path: '/base/category/list',
});
},
'GET /api/BLOCK_NAME/province': getProvince,
'GET /api/BLOCK_NAME/city/:province': getCity,
};
......@@ -10,19 +10,19 @@ const nullSlectItem = {
key: '',
};
@connect(({ geographic }) => {
const { province, isLoading, city } = geographic;
@connect(({ BLOCK_NAME_CAMEL_CASE, loading }) => {
const { province, city } = BLOCK_NAME_CAMEL_CASE;
return {
province,
city,
isLoading,
loading: loading.models.BLOCK_NAME_CAMEL_CASE,
};
})
class GeographicView extends PureComponent {
componentDidMount = () => {
const { dispatch } = this.props;
dispatch({
type: 'geographic/fetchProvince',
type: 'BLOCK_NAME_CAMEL_CASE/fetchProvince',
});
};
......@@ -31,7 +31,7 @@ class GeographicView extends PureComponent {
if (!props.value && !!value && !!value.province) {
dispatch({
type: 'geographic/fetchCity',
type: 'BLOCK_NAME_CAMEL_CASE/fetchCity',
payload: value.province.key,
});
}
......@@ -65,7 +65,7 @@ class GeographicView extends PureComponent {
selectProvinceItem = item => {
const { dispatch, onChange } = this.props;
dispatch({
type: 'geographic/fetchCity',
type: 'BLOCK_NAME_CAMEL_CASE/fetchCity',
payload: item.key,
});
onChange({
......@@ -99,9 +99,9 @@ class GeographicView extends PureComponent {
render() {
const { province, city } = this.conversionObject();
const { isLoading } = this.props;
const { loading } = this.props;
return (
<Spin spinning={isLoading} wrapperClassName={styles.row}>
<Spin spinning={loading} wrapperClassName={styles.row}>
<Select
className={styles.item}
value={province}
......
......@@ -5,7 +5,6 @@ import { connect } from 'dva';
import styles from './BaseView.less';
import GeographicView from './GeographicView';
import PhoneView from './PhoneView';
// import { getTimeDistance } from '@/utils/utils';
const FormItem = Form.Item;
const { Option } = Select;
......@@ -14,7 +13,7 @@ const { Option } = Select;
const AvatarView = ({ avatar }) => (
<Fragment>
<div className={styles.avatar_title}>
<FormattedMessage id="app.settings.basic.avatar" defaultMessage="Avatar" />
<FormattedMessage id="BLOCK_NAME.basic.avatar" defaultMessage="Avatar" />
</div>
<div className={styles.avatar}>
<img src={avatar} alt="avatar" />
......@@ -22,7 +21,7 @@ const AvatarView = ({ avatar }) => (
<Upload fileList={[]}>
<div className={styles.button_view}>
<Button icon="upload">
<FormattedMessage id="app.settings.basic.change-avatar" defaultMessage="Change avatar" />
<FormattedMessage id="BLOCK_NAME.basic.change-avatar" defaultMessage="Change avatar" />
</Button>
</div>
</Upload>
......@@ -51,8 +50,8 @@ const validatorPhone = (rule, value, callback) => {
callback();
};
@connect(({ user }) => ({
currentUser: user.currentUser,
@connect(({ BLOCK_NAME_CAMEL_CASE }) => ({
currentUser: BLOCK_NAME_CAMEL_CASE.currentUser,
}))
@Form.create()
class BaseView extends Component {
......@@ -90,47 +89,47 @@ class BaseView extends Component {
<div className={styles.baseView} ref={this.getViewDom}>
<div className={styles.left}>
<Form layout="vertical" onSubmit={this.handleSubmit} hideRequiredMark>
<FormItem label={formatMessage({ id: 'app.settings.basic.email' })}>
<FormItem label={formatMessage({ id: 'BLOCK_NAME.basic.email' })}>
{getFieldDecorator('email', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.email-message' }, {}),
message: formatMessage({ id: 'BLOCK_NAME.basic.email-message' }, {}),
},
],
})(<Input />)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.nickname' })}>
<FormItem label={formatMessage({ id: 'BLOCK_NAME.basic.nickname' })}>
{getFieldDecorator('name', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.nickname-message' }, {}),
message: formatMessage({ id: 'BLOCK_NAME.basic.nickname-message' }, {}),
},
],
})(<Input />)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.profile' })}>
<FormItem label={formatMessage({ id: 'BLOCK_NAME.basic.profile' })}>
{getFieldDecorator('profile', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.profile-message' }, {}),
message: formatMessage({ id: 'BLOCK_NAME.basic.profile-message' }, {}),
},
],
})(
<Input.TextArea
placeholder={formatMessage({ id: 'app.settings.basic.profile-placeholder' })}
placeholder={formatMessage({ id: 'BLOCK_NAME.basic.profile-placeholder' })}
rows={4}
/>
)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.country' })}>
<FormItem label={formatMessage({ id: 'BLOCK_NAME.basic.country' })}>
{getFieldDecorator('country', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.country-message' }, {}),
message: formatMessage({ id: 'BLOCK_NAME.basic.country-message' }, {}),
},
],
})(
......@@ -139,12 +138,12 @@ class BaseView extends Component {
</Select>
)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.geographic' })}>
<FormItem label={formatMessage({ id: 'BLOCK_NAME.basic.geographic' })}>
{getFieldDecorator('geographic', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.geographic-message' }, {}),
message: formatMessage({ id: 'BLOCK_NAME.basic.geographic-message' }, {}),
},
{
validator: validatorGeographic,
......@@ -152,22 +151,22 @@ class BaseView extends Component {
],
})(<GeographicView />)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.address' })}>
<FormItem label={formatMessage({ id: 'BLOCK_NAME.basic.address' })}>
{getFieldDecorator('address', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.address-message' }, {}),
message: formatMessage({ id: 'BLOCK_NAME.basic.address-message' }, {}),
},
],
})(<Input />)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.phone' })}>
<FormItem label={formatMessage({ id: 'BLOCK_NAME.basic.phone' })}>
{getFieldDecorator('phone', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.phone-message' }, {}),
message: formatMessage({ id: 'BLOCK_NAME.basic.phone-message' }, {}),
},
{ validator: validatorPhone },
],
......@@ -175,7 +174,7 @@ class BaseView extends Component {
</FormItem>
<Button type="primary">
<FormattedMessage
id="app.settings.basic.update"
id="BLOCK_NAME.basic.update"
defaultMessage="Update Information"
/>
</Button>
......
......@@ -5,31 +5,31 @@ import { Icon, List } from 'antd';
class BindingView extends Component {
getData = () => [
{
title: formatMessage({ id: 'app.settings.binding.taobao' }, {}),
description: formatMessage({ id: 'app.settings.binding.taobao-description' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.binding.taobao' }, {}),
description: formatMessage({ id: 'BLOCK_NAME.binding.taobao-description' }, {}),
actions: [
<a>
<FormattedMessage id="app.settings.binding.bind" defaultMessage="Bind" />
<FormattedMessage id="BLOCK_NAME.binding.bind" defaultMessage="Bind" />
</a>,
],
avatar: <Icon type="taobao" className="taobao" />,
},
{
title: formatMessage({ id: 'app.settings.binding.alipay' }, {}),
description: formatMessage({ id: 'app.settings.binding.alipay-description' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.binding.alipay' }, {}),
description: formatMessage({ id: 'BLOCK_NAME.binding.alipay-description' }, {}),
actions: [
<a>
<FormattedMessage id="app.settings.binding.bind" defaultMessage="Bind" />
<FormattedMessage id="BLOCK_NAME.binding.bind" defaultMessage="Bind" />
</a>,
],
avatar: <Icon type="alipay" className="alipay" />,
},
{
title: formatMessage({ id: 'app.settings.binding.dingding' }, {}),
description: formatMessage({ id: 'app.settings.binding.dingding-description' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.binding.dingding' }, {}),
description: formatMessage({ id: 'BLOCK_NAME.binding.dingding-description' }, {}),
actions: [
<a>
<FormattedMessage id="app.settings.binding.bind" defaultMessage="Bind" />
<FormattedMessage id="BLOCK_NAME.binding.bind" defaultMessage="Bind" />
</a>,
],
avatar: <Icon type="dingding" className="dingding" />,
......
......@@ -6,25 +6,25 @@ class NotificationView extends Component {
getData = () => {
const Action = (
<Switch
checkedChildren={formatMessage({ id: 'app.settings.open' })}
unCheckedChildren={formatMessage({ id: 'app.settings.close' })}
checkedChildren={formatMessage({ id: 'BLOCK_NAME.settings.open' })}
unCheckedChildren={formatMessage({ id: 'BLOCK_NAME.settings.close' })}
defaultChecked
/>
);
return [
{
title: formatMessage({ id: 'app.settings.notification.password' }, {}),
description: formatMessage({ id: 'app.settings.notification.password-description' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.notification.password' }, {}),
description: formatMessage({ id: 'BLOCK_NAME.notification.password-description' }, {}),
actions: [Action],
},
{
title: formatMessage({ id: 'app.settings.notification.messages' }, {}),
description: formatMessage({ id: 'app.settings.notification.messages-description' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.notification.messages' }, {}),
description: formatMessage({ id: 'BLOCK_NAME.notification.messages-description' }, {}),
actions: [Action],
},
{
title: formatMessage({ id: 'app.settings.notification.todo' }, {}),
description: formatMessage({ id: 'app.settings.notification.todo-description' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.notification.todo' }, {}),
description: formatMessage({ id: 'BLOCK_NAME.notification.todo-description' }, {}),
actions: [Action],
},
];
......
......@@ -6,17 +6,17 @@ import { List } from 'antd';
const passwordStrength = {
strong: (
<font className="strong">
<FormattedMessage id="app.settings.security.strong" defaultMessage="Strong" />
<FormattedMessage id="BLOCK_NAME.security.strong" defaultMessage="Strong" />
</font>
),
medium: (
<font className="medium">
<FormattedMessage id="app.settings.security.medium" defaultMessage="Medium" />
<FormattedMessage id="BLOCK_NAME.security.medium" defaultMessage="Medium" />
</font>
),
weak: (
<font className="weak">
<FormattedMessage id="app.settings.security.weak" defaultMessage="Weak" />
<FormattedMessage id="BLOCK_NAME.security.weak" defaultMessage="Weak" />
Weak
</font>
),
......@@ -25,58 +25,58 @@ const passwordStrength = {
class SecurityView extends Component {
getData = () => [
{
title: formatMessage({ id: 'app.settings.security.password' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.security.password' }, {}),
description: (
<Fragment>
{formatMessage({ id: 'app.settings.security.password-description' })}
{formatMessage({ id: 'BLOCK_NAME.security.password-description' })}
{passwordStrength.strong}
</Fragment>
),
actions: [
<a>
<FormattedMessage id="app.settings.security.modify" defaultMessage="Modify" />
<FormattedMessage id="BLOCK_NAME.security.modify" defaultMessage="Modify" />
</a>,
],
},
{
title: formatMessage({ id: 'app.settings.security.phone' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.security.phone' }, {}),
description: `${formatMessage(
{ id: 'app.settings.security.phone-description' },
{ id: 'BLOCK_NAME.security.phone-description' },
{}
)}:138****8293`,
actions: [
<a>
<FormattedMessage id="app.settings.security.modify" defaultMessage="Modify" />
<FormattedMessage id="BLOCK_NAME.security.modify" defaultMessage="Modify" />
</a>,
],
},
{
title: formatMessage({ id: 'app.settings.security.question' }, {}),
description: formatMessage({ id: 'app.settings.security.question-description' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.security.question' }, {}),
description: formatMessage({ id: 'BLOCK_NAME.security.question-description' }, {}),
actions: [
<a>
<FormattedMessage id="app.settings.security.set" defaultMessage="Set" />
<FormattedMessage id="BLOCK_NAME.security.set" defaultMessage="Set" />
</a>,
],
},
{
title: formatMessage({ id: 'app.settings.security.email' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.security.email' }, {}),
description: `${formatMessage(
{ id: 'app.settings.security.email-description' },
{ id: 'BLOCK_NAME.security.email-description' },
{}
)}:ant***sign.com`,
actions: [
<a>
<FormattedMessage id="app.settings.security.modify" defaultMessage="Modify" />
<FormattedMessage id="BLOCK_NAME.security.modify" defaultMessage="Modify" />
</a>,
],
},
{
title: formatMessage({ id: 'app.settings.security.mfa' }, {}),
description: formatMessage({ id: 'app.settings.security.mfa-description' }, {}),
title: formatMessage({ id: 'BLOCK_NAME.security.mfa' }, {}),
description: formatMessage({ id: 'BLOCK_NAME.security.mfa-description' }, {}),
actions: [
<a>
<FormattedMessage id="app.settings.security.bind" defaultMessage="Bind" />
<FormattedMessage id="BLOCK_NAME.security.bind" defaultMessage="Bind" />
</a>,
],
},
......
import React, { Component } from 'react';
import { connect } from 'dva';
import router from 'umi/router';
import { FormattedMessage } from 'umi/locale';
import { Menu } from 'antd';
import GridContent from '@/components/PageHeaderWrapper/GridContent';
import styles from './Info.less';
import styles from './style.less';
import BaseView from './components/base';
import SecurityView from './components/security';
import BindingView from './components/binding';
import NotificationView from './components/notification';
const { Item } = Menu;
@connect(({ user }) => ({
currentUser: user.currentUser,
@connect(({ BLOCK_NAME_CAMEL_CASE }) => ({
currentUser: BLOCK_NAME_CAMEL_CASE.currentUser,
}))
class Info extends Component {
class PAGE_NAME_UPPER_CAMEL_CASE extends Component {
constructor(props) {
super(props);
const { match, location } = props;
const menuMap = {
base: <FormattedMessage id="app.settings.menuMap.basic" defaultMessage="Basic Settings" />,
base: <FormattedMessage id="BLOCK_NAME.menuMap.basic" defaultMessage="Basic Settings" />,
security: (
<FormattedMessage id="app.settings.menuMap.security" defaultMessage="Security Settings" />
<FormattedMessage id="BLOCK_NAME.menuMap.security" defaultMessage="Security Settings" />
),
binding: (
<FormattedMessage id="app.settings.menuMap.binding" defaultMessage="Account Binding" />
<FormattedMessage id="BLOCK_NAME.menuMap.binding" defaultMessage="Account Binding" />
),
notification: (
<FormattedMessage
id="app.settings.menuMap.notification"
id="BLOCK_NAME.menuMap.notification"
defaultMessage="New Message Notification"
/>
),
};
const key = location.pathname.replace(`${match.path}/`, '');
this.state = {
mode: 'inline',
menuMap,
selectKey: menuMap[key] ? key : 'base',
selectKey: 'base',
};
}
static getDerivedStateFromProps(props, state) {
const { match, location } = props;
let selectKey = location.pathname.replace(`${match.path}/`, '');
selectKey = state.menuMap[selectKey] ? selectKey : 'base';
if (selectKey !== state.selectKey) {
return { selectKey };
}
return null;
}
componentDidMount() {
const { dispatch } = this.props;
dispatch({
type: 'BLOCK_NAME_CAMEL_CASE/fetchCurrent',
});
window.addEventListener('resize', this.resize);
this.resize();
}
......@@ -68,7 +62,6 @@ class Info extends Component {
};
selectKey = ({ key }) => {
router.push(`/account/settings/${key}`);
this.setState({
selectKey: key,
});
......@@ -79,6 +72,9 @@ class Info extends Component {
return;
}
requestAnimationFrame(() => {
if (!this.main) {
return;
}
let mode = 'inline';
const { offsetWidth } = this.main;
if (this.main.offsetWidth < 641 && offsetWidth > 400) {
......@@ -93,33 +89,49 @@ class Info extends Component {
});
};
renderChildren = () => {
const { selectKey } = this.state;
switch (selectKey) {
case 'base':
return <BaseView />;
case 'security':
return <SecurityView />;
case 'binding':
return <BindingView />;
case 'notification':
return <NotificationView />;
default:
break;
}
return null;
};
render() {
const { children, currentUser } = this.props;
const { currentUser } = this.props;
if (!currentUser.userid) {
return '';
}
const { mode, selectKey } = this.state;
return (
<GridContent>
<div
className={styles.main}
ref={ref => {
this.main = ref;
}}
>
<div className={styles.leftmenu}>
<Menu mode={mode} selectedKeys={[selectKey]} onClick={this.selectKey}>
{this.getmenu()}
</Menu>
</div>
<div className={styles.right}>
<div className={styles.title}>{this.getRightTitle()}</div>
{children}
</div>
<div
className={styles.main}
ref={ref => {
this.main = ref;
}}
>
<div className={styles.leftmenu}>
<Menu mode={mode} selectedKeys={[selectKey]} onClick={this.selectKey}>
{this.getmenu()}
</Menu>
</div>
<div className={styles.right}>
<div className={styles.title}>{this.getRightTitle()}</div>
{this.renderChildren()}
</div>
</GridContent>
</div>
);
}
}
export default Info;
export default PAGE_NAME_UPPER_CAMEL_CASE;
export default {
'BLOCK_NAME.menuMap.basic': 'Basic Settings',
'BLOCK_NAME.menuMap.security': 'Security Settings',
'BLOCK_NAME.menuMap.binding': 'Account Binding',
'BLOCK_NAME.menuMap.notification': 'New Message Notification',
'BLOCK_NAME.basic.avatar': 'Avatar',
'BLOCK_NAME.basic.change-avatar': 'Change avatar',
'BLOCK_NAME.basic.email': 'Email',
'BLOCK_NAME.basic.email-message': 'Please input your email!',
'BLOCK_NAME.basic.nickname': 'Nickname',
'BLOCK_NAME.basic.nickname-message': 'Please input your Nickname!',
'BLOCK_NAME.basic.profile': 'Personal profile',
'BLOCK_NAME.basic.profile-message': 'Please input your personal profile!',
'BLOCK_NAME.basic.profile-placeholder': 'Brief introduction to yourself',
'BLOCK_NAME.basic.country': 'Country/Region',
'BLOCK_NAME.basic.country-message': 'Please input your country!',
'BLOCK_NAME.basic.geographic': 'Province or city',
'BLOCK_NAME.basic.geographic-message': 'Please input your geographic info!',
'BLOCK_NAME.basic.address': 'Street Address',
'BLOCK_NAME.basic.address-message': 'Please input your address!',
'BLOCK_NAME.basic.phone': 'Phone Number',
'BLOCK_NAME.basic.phone-message': 'Please input your phone!',
'BLOCK_NAME.basic.update': 'Update Information',
'BLOCK_NAME.security.strong': 'Strong',
'BLOCK_NAME.security.medium': 'Medium',
'BLOCK_NAME.security.weak': 'Weak',
'BLOCK_NAME.security.password': 'Account Password',
'BLOCK_NAME.security.password-description': 'Current password strength:',
'BLOCK_NAME.security.phone': 'Security Phone',
'BLOCK_NAME.security.phone-description': 'Bound phone:',
'BLOCK_NAME.security.question': 'Security Question',
'BLOCK_NAME.security.question-description':
'The security question is not set, and the security policy can effectively protect the account security',
'BLOCK_NAME.security.email': 'Backup Email',
'BLOCK_NAME.security.email-description': 'Bound Email:',
'BLOCK_NAME.security.mfa': 'MFA Device',
'BLOCK_NAME.security.mfa-description':
'Unbound MFA device, after binding, can be confirmed twice',
'BLOCK_NAME.security.modify': 'Modify',
'BLOCK_NAME.security.set': 'Set',
'BLOCK_NAME.security.bind': 'Bind',
'BLOCK_NAME.binding.taobao': 'Binding Taobao',
'BLOCK_NAME.binding.taobao-description': 'Currently unbound Taobao account',
'BLOCK_NAME.binding.alipay': 'Binding Alipay',
'BLOCK_NAME.binding.alipay-description': 'Currently unbound Alipay account',
'BLOCK_NAME.binding.dingding': 'Binding DingTalk',
'BLOCK_NAME.binding.dingding-description': 'Currently unbound DingTalk account',
'BLOCK_NAME.binding.bind': 'Bind',
'BLOCK_NAME.notification.password': 'Account Password',
'BLOCK_NAME.notification.password-description':
'Messages from other users will be notified in the form of a station letter',
'BLOCK_NAME.notification.messages': 'System Messages',
'BLOCK_NAME.notification.messages-description':
'System messages will be notified in the form of a station letter',
'BLOCK_NAME.notification.todo': 'To-do Notification',
'BLOCK_NAME.notification.todo-description':
'The to-do list will be notified in the form of a letter from the station',
'BLOCK_NAME.settings.open': 'Open',
'BLOCK_NAME.settings.close': 'Close',
};
export default {
'BLOCK_NAME.menuMap.basic': '基本设置',
'BLOCK_NAME.menuMap.security': '安全设置',
'BLOCK_NAME.menuMap.binding': '账号绑定',
'BLOCK_NAME.menuMap.notification': '新消息通知',
'BLOCK_NAME.basic.avatar': '头像',
'BLOCK_NAME.basic.change-avatar': '更换头像',
'BLOCK_NAME.basic.email': '邮箱',
'BLOCK_NAME.basic.email-message': '请输入您的邮箱!',
'BLOCK_NAME.basic.nickname': '昵称',
'BLOCK_NAME.basic.nickname-message': '请输入您的昵称!',
'BLOCK_NAME.basic.profile': '个人简介',
'BLOCK_NAME.basic.profile-message': '请输入个人简介!',
'BLOCK_NAME.basic.profile-placeholder': '个人简介',
'BLOCK_NAME.basic.country': '国家/地区',
'BLOCK_NAME.basic.country-message': '请输入您的国家或地区!',
'BLOCK_NAME.basic.geographic': '所在省市',
'BLOCK_NAME.basic.geographic-message': '请输入您的所在省市!',
'BLOCK_NAME.basic.address': '街道地址',
'BLOCK_NAME.basic.address-message': '请输入您的街道地址!',
'BLOCK_NAME.basic.phone': '联系电话',
'BLOCK_NAME.basic.phone-message': '请输入您的联系电话!',
'BLOCK_NAME.basic.update': '更新基本信息',
'BLOCK_NAME.security.strong': '',
'BLOCK_NAME.security.medium': '',
'BLOCK_NAME.security.weak': '',
'BLOCK_NAME.security.password': '账户密码',
'BLOCK_NAME.security.password-description': '当前密码强度:',
'BLOCK_NAME.security.phone': '密保手机',
'BLOCK_NAME.security.phone-description': '已绑定手机:',
'BLOCK_NAME.security.question': '密保问题',
'BLOCK_NAME.security.question-description': '未设置密保问题,密保问题可有效保护账户安全',
'BLOCK_NAME.security.email': '备用邮箱',
'BLOCK_NAME.security.email-description': '已绑定邮箱:',
'BLOCK_NAME.security.mfa': 'MFA 设备',
'BLOCK_NAME.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认',
'BLOCK_NAME.security.modify': '修改',
'BLOCK_NAME.security.set': '设置',
'BLOCK_NAME.security.bind': '绑定',
'BLOCK_NAME.binding.taobao': '绑定淘宝',
'BLOCK_NAME.binding.taobao-description': '当前未绑定淘宝账号',
'BLOCK_NAME.binding.alipay': '绑定支付宝',
'BLOCK_NAME.binding.alipay-description': '当前未绑定支付宝账号',
'BLOCK_NAME.binding.dingding': '绑定钉钉',
'BLOCK_NAME.binding.dingding-description': '当前未绑定钉钉账号',
'BLOCK_NAME.binding.bind': '绑定',
'BLOCK_NAME.notification.password': '账户密码',
'BLOCK_NAME.notification.password-description': '其他用户的消息将以站内信的形式通知',
'BLOCK_NAME.notification.messages': '系统消息',
'BLOCK_NAME.notification.messages-description': '系统消息将以站内信的形式通知',
'BLOCK_NAME.notification.todo': '待办任务',
'BLOCK_NAME.notification.todo-description': '待办任务将以站内信的形式通知',
'BLOCK_NAME.settings.open': '',
'BLOCK_NAME.settings.close': '',
};
export default {
'BLOCK_NAME.menuMap.basic': '基本設置',
'BLOCK_NAME.menuMap.security': '安全設置',
'BLOCK_NAME.menuMap.binding': '賬號綁定',
'BLOCK_NAME.menuMap.notification': '新消息通知',
'BLOCK_NAME.basic.avatar': '頭像',
'BLOCK_NAME.basic.change-avatar': '更換頭像',
'BLOCK_NAME.basic.email': '郵箱',
'BLOCK_NAME.basic.email-message': '請輸入您的郵箱!',
'BLOCK_NAME.basic.nickname': '昵稱',
'BLOCK_NAME.basic.nickname-message': '請輸入您的昵稱!',
'BLOCK_NAME.basic.profile': '個人簡介',
'BLOCK_NAME.basic.profile-message': '請輸入個人簡介!',
'BLOCK_NAME.basic.profile-placeholder': '個人簡介',
'BLOCK_NAME.basic.country': '國家/地區',
'BLOCK_NAME.basic.country-message': '請輸入您的國家或地區!',
'BLOCK_NAME.basic.geographic': '所在省市',
'BLOCK_NAME.basic.geographic-message': '請輸入您的所在省市!',
'BLOCK_NAME.basic.address': '街道地址',
'BLOCK_NAME.basic.address-message': '請輸入您的街道地址!',
'BLOCK_NAME.basic.phone': '聯系電話',
'BLOCK_NAME.basic.phone-message': '請輸入您的聯系電話!',
'BLOCK_NAME.basic.update': '更新基本信息',
'BLOCK_NAME.security.strong': '',
'BLOCK_NAME.security.medium': '',
'BLOCK_NAME.security.weak': '',
'BLOCK_NAME.security.password': '賬戶密碼',
'BLOCK_NAME.security.password-description': '當前密碼強度:',
'BLOCK_NAME.security.phone': '密保手機',
'BLOCK_NAME.security.phone-description': '已綁定手機:',
'BLOCK_NAME.security.question': '密保問題',
'BLOCK_NAME.security.question-description': '未設置密保問題,密保問題可有效保護賬戶安全',
'BLOCK_NAME.security.email': '備用郵箱',
'BLOCK_NAME.security.email-description': '已綁定郵箱:',
'BLOCK_NAME.security.mfa': 'MFA 設備',
'BLOCK_NAME.security.mfa-description': '未綁定 MFA 設備,綁定後,可以進行二次確認',
'BLOCK_NAME.security.modify': '修改',
'BLOCK_NAME.security.set': '設置',
'BLOCK_NAME.security.bind': '綁定',
'BLOCK_NAME.binding.taobao': '綁定淘寶',
'BLOCK_NAME.binding.taobao-description': '當前未綁定淘寶賬號',
'BLOCK_NAME.binding.alipay': '綁定支付寶',
'BLOCK_NAME.binding.alipay-description': '當前未綁定支付寶賬號',
'BLOCK_NAME.binding.dingding': '綁定釘釘',
'BLOCK_NAME.binding.dingding-description': '當前未綁定釘釘賬號',
'BLOCK_NAME.binding.bind': '綁定',
'BLOCK_NAME.notification.password': '賬戶密碼',
'BLOCK_NAME.notification.password-description': '其他用戶的消息將以站內信的形式通知',
'BLOCK_NAME.notification.messages': '系統消息',
'BLOCK_NAME.notification.messages-description': '系統消息將以站內信的形式通知',
'BLOCK_NAME.notification.todo': '待辦任務',
'BLOCK_NAME.notification.todo-description': '待辦任務將以站內信的形式通知',
'BLOCK_NAME.settings.open': '',
'BLOCK_NAME.settings.close': '',
};
import { queryProvince, queryCity } from '@/services/geographic';
import { query as queryUsers, queryCurrent, queryProvince, queryCity } from './service';
export default {
namespace: 'geographic',
namespace: 'BLOCK_NAME_CAMEL_CASE',
state: {
list: [],
currentUser: {},
province: [],
city: [],
isLoading: false,
},
effects: {
*fetch(_, { call, put }) {
const response = yield call(queryUsers);
yield put({
type: 'save',
payload: response,
});
},
*fetchCurrent(_, { call, put }) {
const response = yield call(queryCurrent);
yield put({
type: 'saveCurrentUser',
payload: response,
});
},
*fetchProvince(_, { call, put }) {
yield put({
type: 'changeLoading',
......@@ -20,29 +35,39 @@ export default {
type: 'setProvince',
payload: response,
});
yield put({
type: 'changeLoading',
payload: false,
});
},
*fetchCity({ payload }, { call, put }) {
yield put({
type: 'changeLoading',
payload: true,
});
const response = yield call(queryCity, payload);
yield put({
type: 'setCity',
payload: response,
});
yield put({
type: 'changeLoading',
payload: false,
});
},
},
reducers: {
save(state, action) {
return {
...state,
list: action.payload,
};
},
saveCurrentUser(state, action) {
return {
...state,
currentUser: action.payload || {},
};
},
changeNotifyCount(state, action) {
return {
...state,
currentUser: {
...state.currentUser,
notifyCount: action.payload.totalCount,
unreadCount: action.payload.unreadCount,
},
};
},
setProvince(state, action) {
return {
...state,
......
import request from 'umi-request';
export async function queryCurrent() {
return request('/api/BLOCK_NAME/currentUser');
}
export async function queryProvince() {
return request('/api/BLOCK_NAME/province');
}
export async function queryCity(province) {
return request(`/api/BLOCK_NAME/city/${province}`);
}
export async function query() {
return request('/api/BLOCK_NAME/users');
}
/yarn.lock
/package-lock.json
/dist
/node_modules
.umi
.umi-production
export default {
plugins: [
['umi-plugin-block-dev', {
layout: 'ant-design-pro',
}],
['umi-plugin-react', {
dva: true,
locale: true,
antd: true,
}]
],
}
# @umi-blocks/ant-design-pro/advancedform
AdvancedForm
## Usage
```sh
umi block add ant-design-pro/advancedform
```
## SNAPSHOT
![SNAPSHOT](./snapshot.png)
## LICENSE
MIT
{
"name": "@umi-block/advanced-form",
"version": "0.0.1",
"description": "AdvancedForm",
"main": "src/index.js",
"scripts": {
"dev": "umi dev"
},
"repository": {
"type": "git",
"url": "https://github.com/umijs/umi-blocks/ant-design-pro/advancedform"
},
"dependencies": {
"ant-design-pro": "^2.1.1",
"antd": "^3.10.9",
"dva": "^2.4.0",
"lodash": "^4.17.10",
"react": "^16.6.3",
"umi-request": "^1.0.0"
},
"devDependencies": {
"umi": "^2.3.0-beta.1",
"umi-plugin-react": "^1.3.0-beta.1",
"umi-plugin-block-dev": "^1.0.0"
},
"license": "ISC"
}
export default {
'POST /api/BLOCK_NAME/forms': (req, res) => {
res.send({ message: 'Ok' });
},
};
import React from 'react';
import { FormattedMessage } from 'umi/locale';
import Link from 'umi/link';
import { PageHeader } from 'ant-design-pro';
import styles from './index.less';
const PageHeaderWrapper = ({ children, wrapperClassName, ...restProps }) => (
<div style={{ margin: '-24px -24px 0' }} className={wrapperClassName}>
<PageHeader
home={<FormattedMessage id="BLOCK_NAME.menu.home" defaultMessage="Home" />}
key="pageheader"
{...restProps}
linkElement={Link}
itemRender={item => {
if (item.locale) {
return <FormattedMessage id={item.locale} defaultMessage={item.title} />;
}
return item.title;
}}
/>
{children ? <div className={styles.content}>{children}</div> : null}
</div>
);
export default PageHeaderWrapper;
import React, { PureComponent, Fragment } from 'react';
import { Table, Button, Input, message, Popconfirm, Divider } from 'antd';
import isEqual from 'lodash/isEqual';
import styles from './style.less';
import { isEqual } from 'lodash';
import styles from '../style.less';
class TableForm extends PureComponent {
index = 0;
......
......@@ -13,9 +13,9 @@ import {
Popover,
} from 'antd';
import { connect } from 'dva';
import FooterToolbar from '@/components/FooterToolbar';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import TableForm from './TableForm';
import { FooterToolbar } from 'ant-design-pro';
import PageHeaderWrapper from './components/PageHeaderWrapper';
import TableForm from './components/TableForm';
import styles from './style.less';
const { Option } = Select;
......@@ -58,10 +58,10 @@ const tableData = [
];
@connect(({ loading }) => ({
submitting: loading.effects['form/submitAdvancedForm'],
submitting: loading.effects['BLOCK_NAME_CAMEL_CASE/submitAdvancedForm'],
}))
@Form.create()
class AdvancedForm extends PureComponent {
class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
state = {
width: '100%',
};
......@@ -139,7 +139,7 @@ class AdvancedForm extends PureComponent {
if (!error) {
// submit the values
dispatch({
type: 'form/submitAdvancedForm',
type: 'BLOCK_NAME_CAMEL_CASE/submitAdvancedForm',
payload: values,
});
}
......@@ -320,4 +320,4 @@ class AdvancedForm extends PureComponent {
}
}
export default AdvancedForm;
export default PAGE_NAME_UPPER_CAMEL_CASE;
export default {
'BLOCK_NAME.basic.title': 'Basic form',
'BLOCK_NAME.basic.description':
'Form pages are used to collect or verify information to users, and basic forms are common in scenarios where there are fewer data items.',
'BLOCK_NAME.email.required': 'Please enter your email!',
'BLOCK_NAME.email.wrong-format': 'The email address is in the wrong format!',
'BLOCK_NAME.userName.required': 'Please enter your userName!',
'BLOCK_NAME.password.required': 'Please enter your password!',
'BLOCK_NAME.password.twice': 'The passwords entered twice do not match!',
'BLOCK_NAME.strength.msg':
"Please enter at least 6 characters and don't use passwords that are easy to guess.",
'BLOCK_NAME.strength.strong': 'Strength: strong',
'BLOCK_NAME.strength.medium': 'Strength: medium',
'BLOCK_NAME.strength.short': 'Strength: too short',
'BLOCK_NAME.confirm-password.required': 'Please confirm your password!',
'BLOCK_NAME.phone-number.required': 'Please enter your phone number!',
'BLOCK_NAME.phone-number.wrong-format': 'Malformed phone number!',
'BLOCK_NAME.verification-code.required': 'Please enter the verification code!',
'BLOCK_NAME.title.required': 'Please enter a title',
'BLOCK_NAME.date.required': 'Please select the start and end date',
'BLOCK_NAME.goal.required': 'Please enter a description of the goal',
'BLOCK_NAME.standard.required': 'Please enter a metric',
'BLOCK_NAME.form.get-captcha': 'Get Captcha',
'BLOCK_NAME.captcha.second': 'sec',
'BLOCK_NAME.form.optional': ' (optional) ',
'BLOCK_NAME.form.submit': 'Submit',
'BLOCK_NAME.form.save': 'Save',
'BLOCK_NAME.email.placeholder': 'Email',
'BLOCK_NAME.password.placeholder': 'Password',
'BLOCK_NAME.confirm-password.placeholder': 'Confirm password',
'BLOCK_NAME.phone-number.placeholder': 'Phone number',
'BLOCK_NAME.verification-code.placeholder': 'Verification code',
'BLOCK_NAME.title.label': 'Title',
'BLOCK_NAME.title.placeholder': 'Give the target a name',
'BLOCK_NAME.date.label': 'Start and end date',
'BLOCK_NAME.placeholder.start': 'Start date',
'BLOCK_NAME.placeholder.end': 'End date',
'BLOCK_NAME.goal.label': 'Goal description',
'BLOCK_NAME.goal.placeholder': 'Please enter your work goals',
'BLOCK_NAME.standard.label': 'Metrics',
'BLOCK_NAME.standard.placeholder': 'Please enter a metric',
'BLOCK_NAME.client.label': 'Client',
'BLOCK_NAME.label.tooltip': 'Target service object',
'BLOCK_NAME.client.placeholder':
'Please describe your customer service, internal customers directly @ Name / job number',
'BLOCK_NAME.invites.label': 'Inviting critics',
'BLOCK_NAME.invites.placeholder': 'Please direct @ Name / job number, you can invite up to 5 people',
'BLOCK_NAME.weight.label': 'Weight',
'BLOCK_NAME.weight.placeholder': 'Please enter weight',
'BLOCK_NAME.public.label': 'Target disclosure',
'BLOCK_NAME.label.help': 'Customers and invitees are shared by default',
'BLOCK_NAME.radio.public': 'Public',
'BLOCK_NAME.radio.partially-public': 'Partially public',
'BLOCK_NAME.radio.private': 'Private',
'BLOCK_NAME.publicUsers.placeholder': 'Open to',
'BLOCK_NAME.option.A': 'Colleague A',
'BLOCK_NAME.option.B': 'Colleague B',
'BLOCK_NAME.option.C': 'Colleague C',
};
export default {
'BLOCK_NAME.basic.title': 'Basic form',
'BLOCK_NAME.basic.description':
'Form pages are used to collect or verify information to users, and basic forms are common in scenarios where there are fewer data items.',
'BLOCK_NAME.email.required': 'Por favor insira seu email!',
'BLOCK_NAME.email.wrong-format': 'O email está errado!',
'BLOCK_NAME.userName.required': 'Por favor insira nome de usuário!',
'BLOCK_NAME.password.required': 'Por favor insira sua senha!',
'BLOCK_NAME.password.twice': 'As senhas não estão iguais!',
'BLOCK_NAME.strength.msg':
'Por favor insira pelo menos 6 caracteres e não use senhas fáceis de adivinhar.',
'BLOCK_NAME.strength.strong': 'Força: forte',
'BLOCK_NAME.strength.medium': 'Força: média',
'BLOCK_NAME.strength.short': 'Força: curta',
'BLOCK_NAME.confirm-password.required': 'Por favor confirme sua senha!',
'BLOCK_NAME.phone-number.required': 'Por favor insira seu telefone!',
'BLOCK_NAME.phone-number.wrong-format': 'Formato de telefone errado!',
'BLOCK_NAME.verification-code.required': 'Por favor insira seu código de verificação!',
'BLOCK_NAME.form.get-captcha': 'Get Captcha',
'BLOCK_NAME.captcha.second': 'sec',
'BLOCK_NAME.email.placeholder': 'Email',
'BLOCK_NAME.password.placeholder': 'Senha',
'BLOCK_NAME.confirm-password.placeholder': 'Confirme a senha',
'BLOCK_NAME.phone-number.placeholder': 'Telefone',
'BLOCK_NAME.verification-code.placeholder': 'Código de verificação',
'BLOCK_NAME.form.optional': ' (optional) ',
'BLOCK_NAME.form.submit': 'Submit',
'BLOCK_NAME.form.save': 'Save',
'BLOCK_NAME.title.label': 'Title',
'BLOCK_NAME.title.placeholder': 'Give the target a name',
'BLOCK_NAME.date.label': 'Start and end date',
'BLOCK_NAME.placeholder.start': 'Start date',
'BLOCK_NAME.placeholder.end': 'End date',
'BLOCK_NAME.goal.label': 'Goal description',
'BLOCK_NAME.goal.placeholder': 'Please enter your work goals',
'BLOCK_NAME.standard.label': 'Metrics',
'BLOCK_NAME.standard.placeholder': 'Please enter a metric',
'BLOCK_NAME.client.label': 'Client',
'BLOCK_NAME.label.tooltip': 'Target service object',
'BLOCK_NAME.client.placeholder':
'Please describe your customer service, internal customers directly @ Name / job number',
'BLOCK_NAME.invites.label': 'Inviting critics',
'BLOCK_NAME.invites.placeholder': 'Please direct @ Name / job number, you can invite up to 5 people',
'BLOCK_NAME.weight.label': 'Weight',
'BLOCK_NAME.weight.placeholder': 'Please enter weight',
'BLOCK_NAME.public.label': 'Target disclosure',
'BLOCK_NAME.label.help': 'Customers and invitees are shared by default',
'BLOCK_NAME.radio.public': 'Public',
'BLOCK_NAME.radio.partially-public': 'Partially public',
'BLOCK_NAME.radio.private': 'Private',
'BLOCK_NAME.publicUsers.placeholder': 'Open to',
'BLOCK_NAME.option.A': 'Colleague A',
'BLOCK_NAME.option.B': 'Colleague B',
'BLOCK_NAME.option.C': 'Colleague C',
};
export default {
'BLOCK_NAME.basic.title': '基础表单',
'BLOCK_NAME.basic.description':
'表单页用于向用户收集或验证信息,基础表单常见于数据项较少的表单场景。',
'BLOCK_NAME.email.required': '请输入邮箱地址!',
'BLOCK_NAME.email.wrong-format': '邮箱地址格式错误!',
'BLOCK_NAME.userName.required': '请输入用户名!',
'BLOCK_NAME.password.required': '请输入密码!',
'BLOCK_NAME.password.twice': '两次输入的密码不匹配!',
'BLOCK_NAME.strength.msg': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',
'BLOCK_NAME.strength.strong': '强度:强',
'BLOCK_NAME.strength.medium': '强度:中',
'BLOCK_NAME.strength.short': '强度:太短',
'BLOCK_NAME.confirm-password.required': '请确认密码!',
'BLOCK_NAME.phone-number.required': '请输入手机号!',
'BLOCK_NAME.phone-number.wrong-format': '手机号格式错误!',
'BLOCK_NAME.verification-code.required': '请输入验证码!',
'BLOCK_NAME.title.required': '请输入标题',
'BLOCK_NAME.date.required': '请选择起止日期',
'BLOCK_NAME.goal.required': '请输入目标描述',
'BLOCK_NAME.standard.required': '请输入衡量标准',
'BLOCK_NAME.form.get-captcha': '获取验证码',
'BLOCK_NAME.captcha.second': '',
'BLOCK_NAME.form.optional': '(选填)',
'BLOCK_NAME.form.submit': '提交',
'BLOCK_NAME.form.save': '保存',
'BLOCK_NAME.email.placeholder': '邮箱',
'BLOCK_NAME.password.placeholder': '至少6位密码,区分大小写',
'BLOCK_NAME.confirm-password.placeholder': '确认密码',
'BLOCK_NAME.phone-number.placeholder': '手机号',
'BLOCK_NAME.verification-code.placeholder': '验证码',
'BLOCK_NAME.title.label': '标题',
'BLOCK_NAME.title.placeholder': '给目标起个名字',
'BLOCK_NAME.date.label': '起止日期',
'BLOCK_NAME.placeholder.start': '开始日期',
'BLOCK_NAME.placeholder.end': '结束日期',
'BLOCK_NAME.goal.label': '目标描述',
'BLOCK_NAME.goal.placeholder': '请输入你的阶段性工作目标',
'BLOCK_NAME.standard.label': '衡量标准',
'BLOCK_NAME.standard.placeholder': '请输入衡量标准',
'BLOCK_NAME.client.label': '客户',
'BLOCK_NAME.label.tooltip': '目标的服务对象',
'BLOCK_NAME.client.placeholder': '请描述你服务的客户,内部客户直接 @姓名/工号',
'BLOCK_NAME.invites.label': '邀评人',
'BLOCK_NAME.invites.placeholder': '请直接 @姓名/工号,最多可邀请 5 人',
'BLOCK_NAME.weight.label': '权重',
'BLOCK_NAME.weight.placeholder': '请输入',
'BLOCK_NAME.public.label': '目标公开',
'BLOCK_NAME.label.help': '客户、邀评人默认被分享',
'BLOCK_NAME.radio.public': '公开',
'BLOCK_NAME.radio.partially-public': '部分公开',
'BLOCK_NAME.radio.private': '不公开',
'BLOCK_NAME.publicUsers.placeholder': '公开给',
'BLOCK_NAME.option.A': '同事甲',
'BLOCK_NAME.option.B': '同事乙',
'BLOCK_NAME.option.C': '同事丙',
};
export default {
'BLOCK_NAME.basic.title': '基礎表單',
'BLOCK_NAME.basic.description':
'表單頁用於向用戶收集或驗證信息,基礎表單常見於數據項較少的表單場景。',
'BLOCK_NAME.email.required': '請輸入郵箱地址!',
'BLOCK_NAME.email.wrong-format': '郵箱地址格式錯誤!',
'BLOCK_NAME.userName.required': '請輸入賬戶!',
'BLOCK_NAME.password.required': '請輸入密碼!',
'BLOCK_NAME.password.twice': '兩次輸入的密碼不匹配!',
'BLOCK_NAME.strength.msg': '請至少輸入 6 個字符。請不要使用容易被猜到的密碼。',
'BLOCK_NAME.strength.strong': '強度:強',
'BLOCK_NAME.strength.medium': '強度:中',
'BLOCK_NAME.strength.short': '強度:太短',
'BLOCK_NAME.confirm-password.required': '請確認密碼!',
'BLOCK_NAME.phone-number.required': '請輸入手機號!',
'BLOCK_NAME.phone-number.wrong-format': '手機號格式錯誤!',
'BLOCK_NAME.verification-code.required': '請輸入驗證碼!',
'BLOCK_NAME.title.required': '請輸入標題',
'BLOCK_NAME.date.required': '請選擇起止日期',
'BLOCK_NAME.goal.required': '請輸入目標描述',
'BLOCK_NAME.standard.required': '請輸入衡量標淮',
'BLOCK_NAME.form.get-captcha': '獲取驗證碼',
'BLOCK_NAME.captcha.second': '',
'BLOCK_NAME.form.optional': '(選填)',
'BLOCK_NAME.form.submit': '提交',
'BLOCK_NAME.form.save': '保存',
'BLOCK_NAME.email.placeholder': '郵箱',
'BLOCK_NAME.password.placeholder': '至少6位密碼,區分大小寫',
'BLOCK_NAME.confirm-password.placeholder': '確認密碼',
'BLOCK_NAME.phone-number.placeholder': '手機號',
'BLOCK_NAME.verification-code.placeholder': '驗證碼',
'BLOCK_NAME.title.label': '標題',
'BLOCK_NAME.title.placeholder': '給目標起個名字',
'BLOCK_NAME.date.label': '起止日期',
'BLOCK_NAME.placeholder.start': '開始日期',
'BLOCK_NAME.placeholder.end': '結束日期',
'BLOCK_NAME.goal.label': '目標描述',
'BLOCK_NAME.goal.placeholder': '請輸入妳的階段性工作目標',
'BLOCK_NAME.standard.label': '衡量標淮',
'BLOCK_NAME.standard.placeholder': '請輸入衡量標淮',
'BLOCK_NAME.client.label': '客戶',
'BLOCK_NAME.label.tooltip': '目標的服務對象',
'BLOCK_NAME.client.placeholder': '請描述妳服務的客戶,內部客戶直接 @姓名/工號',
'BLOCK_NAME.invites.label': '邀評人',
'BLOCK_NAME.invites.placeholder': '請直接 @姓名/工號,最多可邀請 5 人',
'BLOCK_NAME.weight.label': '權重',
'BLOCK_NAME.weight.placeholder': '請輸入',
'BLOCK_NAME.public.label': '目標公開',
'BLOCK_NAME.label.help': '客戶、邀評人默認被分享',
'BLOCK_NAME.radio.public': '公開',
'BLOCK_NAME.radio.partially-public': '部分公開',
'BLOCK_NAME.radio.private': '不公開',
'BLOCK_NAME.publicUsers.placeholder': '公開給',
'BLOCK_NAME.option.A': '同事甲',
'BLOCK_NAME.option.B': '同事乙',
'BLOCK_NAME.option.C': '同事丙',
};
import { message } from 'antd';
import { fakeSubmitForm } from './service';
export default {
namespace: 'BLOCK_NAME_CAMEL_CASE',
state: {},
effects: {
*submitAdvancedForm({ payload }, { call }) {
yield call(fakeSubmitForm, payload);
message.success('提交成功');
},
},
};
import request from 'umi-request';
export async function fakeSubmitForm(params) {
return request('/api/BLOCK_NAME/forms', {
method: 'POST',
data: params,
});
}
/yarn.lock
/package-lock.json
/dist
/node_modules
.umi
.umi-production
export default {
plugins: [
['umi-plugin-block-dev', {
layout: 'ant-design-pro',
}],
['umi-plugin-react', {
dva: true,
locale: true,
antd: true,
}]
],
}
# @umi-blocks/ant-design-pro/advancedprofile
AdvancedProfile
## Usage
```sh
umi block add ant-design-pro/advancedprofile
```
## SNAPSHOT
![SNAPSHOT](./snapshot.png)
## LICENSE
MIT
{
"name": "@umi-block/advanced-profile",
"version": "0.0.1",
"description": "AdvancedProfile",
"main": "src/index.js",
"scripts": {
"dev": "umi dev"
},
"repository": {
"type": "git",
"url": "https://github.com/umijs/umi-blocks/ant-design-pro/advancedprofile"
},
"dependencies": {
"ant-design-pro": "^2.1.1",
"antd": "^3.10.9",
"classnames": "^2.2.6",
"dva": "^2.4.0",
"lodash-decorators": "^6.0.0",
"react": "^16.6.3",
"umi-request": "^1.0.0"
},
"devDependencies": {
"umi": "^2.3.0-beta.1",
"umi-plugin-react": "^1.3.0-beta.1",
"umi-plugin-block-dev": "^1.0.0"
},
"license": "ISC"
}
const advancedOperation1 = [
{
key: 'op1',
type: '订购关系生效',
name: '曲丽丽',
status: 'agree',
updatedAt: '2017-10-03 19:23:12',
memo: '-',
},
{
key: 'op2',
type: '财务复审',
name: '付小小',
status: 'reject',
updatedAt: '2017-10-03 19:23:12',
memo: '不通过原因',
},
{
key: 'op3',
type: '部门初审',
name: '周毛毛',
status: 'agree',
updatedAt: '2017-10-03 19:23:12',
memo: '-',
},
{
key: 'op4',
type: '提交订单',
name: '林东东',
status: 'agree',
updatedAt: '2017-10-03 19:23:12',
memo: '很棒',
},
{
key: 'op5',
type: '创建订单',
name: '汗牙牙',
status: 'agree',
updatedAt: '2017-10-03 19:23:12',
memo: '-',
},
];
const advancedOperation2 = [
{
key: 'op1',
type: '订购关系生效',
name: '曲丽丽',
status: 'agree',
updatedAt: '2017-10-03 19:23:12',
memo: '-',
},
];
const advancedOperation3 = [
{
key: 'op1',
type: '创建订单',
name: '汗牙牙',
status: 'agree',
updatedAt: '2017-10-03 19:23:12',
memo: '-',
},
];
const getProfileAdvancedData = {
advancedOperation1,
advancedOperation2,
advancedOperation3,
};
export default {
'GET /api/BLOCK_NAME/advanced': getProfileAdvancedData,
};
import React from 'react';
import { FormattedMessage } from 'umi/locale';
import Link from 'umi/link';
import { PageHeader } from 'ant-design-pro';
import styles from './index.less';
const PageHeaderWrapper = ({ children, contentWidth, wrapperClassName, top, ...restProps }) => (
<div style={{ margin: '-24px -24px 0' }} className={wrapperClassName}>
<PageHeader
wide={contentWidth === 'Fixed'}
home={<FormattedMessage id="BLOCK_NAME.menu.home" defaultMessage="Home" />}
key="pageheader"
{...restProps}
linkElement={Link}
itemRender={item => {
if (item.locale) {
return <FormattedMessage id={item.locale} defaultMessage={item.title} />;
}
return item.title;
}}
/>
{children ? <div className={styles.content}>{children}</div> : null}
</div>
);
export default PageHeaderWrapper;
@import '~antd/lib/style/themes/default.less';
.content {
margin: 24px;
padding-top: @layout-header-height;
margin: 24px 24px 0;
}
@media screen and (max-width: @screen-sm) {
.content {
margin: 24px 0 0;
}
}
......@@ -18,9 +18,9 @@ import {
Divider,
} from 'antd';
import classNames from 'classnames';
import DescriptionList from '@/components/DescriptionList';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import styles from './AdvancedProfile.less';
import { DescriptionList } from 'ant-design-pro';
import PageHeaderWrapper from './components/PageHeaderWrapper';
import styles from './style.less';
const { Step } = Steps;
const { Description } = DescriptionList;
......@@ -180,11 +180,11 @@ const columns = [
},
];
@connect(({ profile, loading }) => ({
profile,
loading: loading.effects['profile/fetchAdvanced'],
@connect(({ BLOCK_NAME_CAMEL_CASE, loading }) => ({
BLOCK_NAME_CAMEL_CASE,
loading: loading.effects['BLOCK_NAME_CAMEL_CASE/fetchAdvanced'],
}))
class AdvancedProfile extends Component {
class PAGE_NAME_UPPER_CAMEL_CASE extends Component {
state = {
operationkey: 'tab1',
stepDirection: 'horizontal',
......@@ -193,7 +193,7 @@ class AdvancedProfile extends Component {
componentDidMount() {
const { dispatch } = this.props;
dispatch({
type: 'profile/fetchAdvanced',
type: 'BLOCK_NAME_CAMEL_CASE/fetchAdvanced',
});
this.setStepDirection();
......@@ -227,8 +227,8 @@ class AdvancedProfile extends Component {
render() {
const { stepDirection, operationkey } = this.state;
const { profile, loading } = this.props;
const { advancedOperation1, advancedOperation2, advancedOperation3 } = profile;
const { BLOCK_NAME_CAMEL_CASE, loading } = this.props;
const { advancedOperation1, advancedOperation2, advancedOperation3 } = BLOCK_NAME_CAMEL_CASE;
const contentList = {
tab1: (
<Table
......@@ -350,4 +350,4 @@ class AdvancedProfile extends Component {
}
}
export default AdvancedProfile;
export default PAGE_NAME_UPPER_CAMEL_CASE;
import { queryBasicProfile, queryAdvancedProfile } from '@/services/api';
import { queryAdvancedProfile } from './service';
export default {
namespace: 'profile',
namespace: 'BLOCK_NAME_CAMEL_CASE',
state: {
basicGoods: [],
advancedOperation1: [],
advancedOperation2: [],
advancedOperation3: [],
},
effects: {
*fetchBasic(_, { call, put }) {
const response = yield call(queryBasicProfile);
yield put({
type: 'show',
payload: response,
});
},
*fetchAdvanced(_, { call, put }) {
const response = yield call(queryAdvancedProfile);
yield put({
......
import request from 'umi-request';
export async function queryAdvancedProfile() {
return request('/api/BLOCK_NAME/advanced');
}
/yarn.lock
/package-lock.json
/dist
/node_modules
.umi
.umi-production
export default {
plugins: [
['umi-plugin-block-dev', {
layout: 'ant-design-pro',
}],
['umi-plugin-react', {
dva: true,
locale: true,
antd: true,
}]
],
}
# @umi-blocks/ant-design-pro/analysis
Analysis
## Usage
```sh
umi block add ant-design-pro/analysis
```
## SNAPSHOT
![SNAPSHOT](./snapshot.png)
## LICENSE
MIT
{
"name": "@umi-block/analysis",
"version": "0.0.1",
"description": "Analysis",
"main": "src/index.js",
"scripts": {
"dev": "umi dev"
},
"repository": {
"type": "git",
"url": "https://github.com/umijs/umi-blocks/ant-design-pro/analysis"
},
"dependencies": {
"react": "^16.6.3",
"dva": "^2.4.0",
"antd": "^3.10.9",
"moment": "^2.22.2",
"umi-request": "^1.0.0",
"ant-design-pro": "^2.1.1",
"numeral": "^2.0.6"
},
"devDependencies": {
"umi": "^2.3.0-beta.1",
"umi-plugin-react": "^1.3.0-beta.1",
"umi-plugin-block-dev": "^1.1.0"
},
"license": "ISC"
}
......@@ -192,5 +192,5 @@ const getFakeChartData = {
};
export default {
'GET /api/fake_chart_data': getFakeChartData,
'GET /api/BLOCK_NAME/fake_chart_data': getFakeChartData,
};
import React, { memo } from 'react';
import { Row, Col, Icon, Tooltip } from 'antd';
import { FormattedMessage } from 'umi/locale';
import styles from './Analysis.less';
import { ChartCard, MiniArea, MiniBar, MiniProgress, Field } from '@/components/Charts';
import Trend from '@/components/Trend';
import { Charts, Trend } from 'ant-design-pro';
import numeral from 'numeral';
import Yuan from '@/utils/Yuan';
import styles from '../style.less';
import Yuan from '../utils/Yuan';
const { ChartCard, MiniArea, MiniBar, MiniProgress, Field } = Charts;
const topColResponsiveProps = {
xs: 24,
......@@ -21,10 +22,10 @@ const IntroduceRow = memo(({ loading, visitData }) => (
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
title={<FormattedMessage id="app.analysis.total-sales" defaultMessage="Total Sales" />}
title={<FormattedMessage id="BLOCK_NAME.analysis.total-sales" defaultMessage="Total Sales" />}
action={
<Tooltip
title={<FormattedMessage id="app.analysis.introduce" defaultMessage="Introduce" />}
title={<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />}
>
<Icon type="info-circle-o" />
</Tooltip>
......@@ -33,18 +34,18 @@ const IntroduceRow = memo(({ loading, visitData }) => (
total={() => <Yuan>126560</Yuan>}
footer={
<Field
label={<FormattedMessage id="app.analysis.day-sales" defaultMessage="Daily Sales" />}
label={<FormattedMessage id="BLOCK_NAME.analysis.day-sales" defaultMessage="Daily Sales" />}
value={`¥${numeral(12423).format('0,0')}`}
/>
}
contentHeight={46}
>
<Trend flag="up" style={{ marginRight: 16 }}>
<FormattedMessage id="app.analysis.week" defaultMessage="Weekly Changes" />
<FormattedMessage id="BLOCK_NAME.analysis.week" defaultMessage="Weekly Changes" />
<span className={styles.trendText}>12%</span>
</Trend>
<Trend flag="down">
<FormattedMessage id="app.analysis.day" defaultMessage="Daily Changes" />
<FormattedMessage id="BLOCK_NAME.analysis.day" defaultMessage="Daily Changes" />
<span className={styles.trendText}>11%</span>
</Trend>
</ChartCard>
......@@ -54,10 +55,10 @@ const IntroduceRow = memo(({ loading, visitData }) => (
<ChartCard
bordered={false}
loading={loading}
title={<FormattedMessage id="app.analysis.visits" defaultMessage="Visits" />}
title={<FormattedMessage id="BLOCK_NAME.analysis.visits" defaultMessage="Visits" />}
action={
<Tooltip
title={<FormattedMessage id="app.analysis.introduce" defaultMessage="Introduce" />}
title={<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />}
>
<Icon type="info-circle-o" />
</Tooltip>
......@@ -65,7 +66,7 @@ const IntroduceRow = memo(({ loading, visitData }) => (
total={numeral(8846).format('0,0')}
footer={
<Field
label={<FormattedMessage id="app.analysis.day-visits" defaultMessage="Daily Visits" />}
label={<FormattedMessage id="BLOCK_NAME.analysis.day-visits" defaultMessage="Daily Visits" />}
value={numeral(1234).format('0,0')}
/>
}
......@@ -78,10 +79,10 @@ const IntroduceRow = memo(({ loading, visitData }) => (
<ChartCard
bordered={false}
loading={loading}
title={<FormattedMessage id="app.analysis.payments" defaultMessage="Payments" />}
title={<FormattedMessage id="BLOCK_NAME.analysis.payments" defaultMessage="Payments" />}
action={
<Tooltip
title={<FormattedMessage id="app.analysis.introduce" defaultMessage="Introduce" />}
title={<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />}
>
<Icon type="info-circle-o" />
</Tooltip>
......@@ -91,7 +92,7 @@ const IntroduceRow = memo(({ loading, visitData }) => (
<Field
label={
<FormattedMessage
id="app.analysis.conversion-rate"
id="BLOCK_NAME.analysis.conversion-rate"
defaultMessage="Conversion Rate"
/>
}
......@@ -109,13 +110,13 @@ const IntroduceRow = memo(({ loading, visitData }) => (
bordered={false}
title={
<FormattedMessage
id="app.analysis.operational-effect"
id="BLOCK_NAME.analysis.operational-effect"
defaultMessage="Operational Effect"
/>
}
action={
<Tooltip
title={<FormattedMessage id="app.analysis.introduce" defaultMessage="Introduce" />}
title={<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="Introduce" />}
>
<Icon type="info-circle-o" />
</Tooltip>
......@@ -124,11 +125,11 @@ const IntroduceRow = memo(({ loading, visitData }) => (
footer={
<div style={{ whiteSpace: 'nowrap', overflow: 'hidden' }}>
<Trend flag="up" style={{ marginRight: 16 }}>
<FormattedMessage id="app.analysis.week" defaultMessage="Weekly Changes" />
<FormattedMessage id="BLOCK_NAME.analysis.week" defaultMessage="Weekly Changes" />
<span className={styles.trendText}>12%</span>
</Trend>
<Trend flag="down">
<FormattedMessage id="app.analysis.day" defaultMessage="Weekly Changes" />
<FormattedMessage id="BLOCK_NAME.analysis.day" defaultMessage="Weekly Changes" />
<span className={styles.trendText}>11%</span>
</Trend>
</div>
......
import React, { memo } from 'react';
import { Card, Tabs, Row, Col } from 'antd';
import { formatMessage, FormattedMessage } from 'umi/locale';
import styles from './Analysis.less';
import { TimelineChart, Pie } from '@/components/Charts';
import NumberInfo from '@/components/NumberInfo';
import { Charts, NumberInfo } from 'ant-design-pro';
import styles from '../style.less';
const { TimelineChart, Pie } = Charts;
const CustomTab = ({ data, currentTabKey: currentKey }) => (
<Row gutter={8} style={{ width: 138, margin: '8px 0' }}>
......@@ -11,7 +12,7 @@ const CustomTab = ({ data, currentTabKey: currentKey }) => (
<NumberInfo
title={data.name}
subTitle={
<FormattedMessage id="app.analysis.conversion-rate" defaultMessage="Conversion Rate" />
<FormattedMessage id="BLOCK_NAME.analysis.conversion-rate" defaultMessage="Conversion Rate" />
}
gap={2}
total={`${data.cvr * 100}%`}
......@@ -50,8 +51,8 @@ const OfflineData = memo(
height={400}
data={offlineChartData}
titleMap={{
y1: formatMessage({ id: 'app.analysis.traffic' }),
y2: formatMessage({ id: 'app.analysis.payments' }),
y1: formatMessage({ id: 'BLOCK_NAME.analysis.traffic' }),
y2: formatMessage({ id: 'BLOCK_NAME.analysis.payments' }),
}}
/>
</div>
......
import React, { memo } from 'react';
import { Card, Radio } from 'antd';
import { Charts } from 'ant-design-pro';
import { FormattedMessage } from 'umi/locale';
import styles from './Analysis.less';
import { Pie } from '@/components/Charts';
import Yuan from '@/utils/Yuan';
import styles from '../style.less';
import Yuan from '../utils/Yuan';
const { Pie } = Charts;
const ProportionSales = memo(
({ dropdownGroup, salesType, loading, salesPieData, handleChangeSalesType }) => (
......@@ -13,7 +15,7 @@ const ProportionSales = memo(
bordered={false}
title={
<FormattedMessage
id="app.analysis.the-proportion-of-sales"
id="BLOCK_NAME.analysis.the-proportion-of-sales"
defaultMessage="The Proportion of Sales"
/>
}
......@@ -24,13 +26,13 @@ const ProportionSales = memo(
<div className={styles.salesTypeRadio}>
<Radio.Group value={salesType} onChange={handleChangeSalesType}>
<Radio.Button value="all">
<FormattedMessage id="app.analysis.channel.all" defaultMessage="ALL" />
<FormattedMessage id="BLOCK_NAME.channel.all" defaultMessage="ALL" />
</Radio.Button>
<Radio.Button value="online">
<FormattedMessage id="app.analysis.channel.online" defaultMessage="Online" />
<FormattedMessage id="BLOCK_NAME.channel.online" defaultMessage="Online" />
</Radio.Button>
<Radio.Button value="stores">
<FormattedMessage id="app.analysis.channel.stores" defaultMessage="Stores" />
<FormattedMessage id="BLOCK_NAME.channel.stores" defaultMessage="Stores" />
</Radio.Button>
</Radio.Group>
</div>
......@@ -38,19 +40,24 @@ const ProportionSales = memo(
}
style={{ marginTop: 24 }}
>
<h4 style={{ marginTop: 10, marginBottom: 32 }}>
<FormattedMessage id="app.analysis.sales" defaultMessage="Sales" />
</h4>
<Pie
hasLegend
subTitle={<FormattedMessage id="app.analysis.sales" defaultMessage="Sales" />}
total={() => <Yuan>{salesPieData.reduce((pre, now) => now.y + pre, 0)}</Yuan>}
data={salesPieData}
valueFormat={value => <Yuan>{value}</Yuan>}
height={270}
lineWidth={4}
style={{ padding: '8px 0' }}
/>
<div
style={{
minHeight: 380,
}}
>
<h4 style={{ marginTop: 8, marginBottom: 32 }}>
<FormattedMessage id="BLOCK_NAME.analysis.sales" defaultMessage="Sales" />
</h4>
<Pie
hasLegend
subTitle={<FormattedMessage id="BLOCK_NAME.analysis.sales" defaultMessage="Sales" />}
total={() => <Yuan>{salesPieData.reduce((pre, now) => now.y + pre, 0)}</Yuan>}
data={salesPieData}
valueFormat={value => <Yuan>{value}</Yuan>}
height={248}
lineWidth={4}
/>
</div>
</Card>
)
);
......
......@@ -2,8 +2,10 @@ import React, { memo } from 'react';
import { Row, Col, Card, Tabs, DatePicker } from 'antd';
import { FormattedMessage, formatMessage } from 'umi/locale';
import numeral from 'numeral';
import styles from './Analysis.less';
import { Bar } from '@/components/Charts';
import { Charts } from 'ant-design-pro';
import styles from '../style.less';
const { Bar } = Charts;
const { RangePicker } = DatePicker;
const { TabPane } = Tabs;
......@@ -11,7 +13,7 @@ const { TabPane } = Tabs;
const rankingListData = [];
for (let i = 0; i < 7; i += 1) {
rankingListData.push({
title: formatMessage({ id: 'app.analysis.test' }, { no: i }),
title: formatMessage({ id: 'BLOCK_NAME.analysis.test' }, { no: i }),
total: 323234,
});
}
......@@ -25,16 +27,16 @@ const SalesCard = memo(
<div className={styles.salesExtraWrap}>
<div className={styles.salesExtra}>
<a className={isActive('today')} onClick={() => selectDate('today')}>
<FormattedMessage id="app.analysis.all-day" defaultMessage="All Day" />
<FormattedMessage id="BLOCK_NAME.analysis.all-day" defaultMessage="All Day" />
</a>
<a className={isActive('week')} onClick={() => selectDate('week')}>
<FormattedMessage id="app.analysis.all-week" defaultMessage="All Week" />
<FormattedMessage id="BLOCK_NAME.analysis.all-week" defaultMessage="All Week" />
</a>
<a className={isActive('month')} onClick={() => selectDate('month')}>
<FormattedMessage id="app.analysis.all-month" defaultMessage="All Month" />
<FormattedMessage id="BLOCK_NAME.analysis.all-month" defaultMessage="All Month" />
</a>
<a className={isActive('year')} onClick={() => selectDate('year')}>
<FormattedMessage id="app.analysis.all-year" defaultMessage="All Year" />
<FormattedMessage id="BLOCK_NAME.analysis.all-year" defaultMessage="All Year" />
</a>
</div>
<RangePicker
......@@ -48,7 +50,7 @@ const SalesCard = memo(
tabBarStyle={{ marginBottom: 24 }}
>
<TabPane
tab={<FormattedMessage id="app.analysis.sales" defaultMessage="Sales" />}
tab={<FormattedMessage id="BLOCK_NAME.analysis.sales" defaultMessage="Sales" />}
key="sales"
>
<Row>
......@@ -58,7 +60,7 @@ const SalesCard = memo(
height={295}
title={
<FormattedMessage
id="app.analysis.sales-trend"
id="BLOCK_NAME.analysis.sales-trend"
defaultMessage="Sales Trend"
/>
}
......@@ -70,7 +72,7 @@ const SalesCard = memo(
<div className={styles.salesRank}>
<h4 className={styles.rankingTitle}>
<FormattedMessage
id="app.analysis.sales-ranking"
id="BLOCK_NAME.analysis.sales-ranking"
defaultMessage="Sales Ranking"
/>
</h4>
......@@ -96,7 +98,7 @@ const SalesCard = memo(
</Row>
</TabPane>
<TabPane
tab={<FormattedMessage id="app.analysis.visits" defaultMessage="Visits" />}
tab={<FormattedMessage id="BLOCK_NAME.analysis.visits" defaultMessage="Visits" />}
key="views"
>
<Row>
......@@ -106,7 +108,7 @@ const SalesCard = memo(
height={292}
title={
<FormattedMessage
id="app.analysis.visits-trend"
id="BLOCK_NAME.analysis.visits-trend"
defaultMessage="Visits Trend"
/>
}
......@@ -118,7 +120,7 @@ const SalesCard = memo(
<div className={styles.salesRank}>
<h4 className={styles.rankingTitle}>
<FormattedMessage
id="app.analysis.visits-ranking"
id="BLOCK_NAME.analysis.visits-ranking"
defaultMessage="Visits Ranking"
/>
</h4>
......
import React, { memo } from 'react';
import { Row, Col, Table, Tooltip, Card, Icon } from 'antd';
import { FormattedMessage } from 'umi/locale';
import Trend from '@/components/Trend';
import { Trend, NumberInfo, Charts } from 'ant-design-pro';
import numeral from 'numeral';
import styles from './Analysis.less';
import NumberInfo from '@/components/NumberInfo';
import { MiniArea } from '@/components/Charts';
import styles from '../style.less';
const { MiniArea } = Charts;
const columns = [
{
title: <FormattedMessage id="app.analysis.table.rank" defaultMessage="Rank" />,
title: <FormattedMessage id="BLOCK_NAME.table.rank" defaultMessage="Rank" />,
dataIndex: 'index',
key: 'index',
},
{
title: (
<FormattedMessage id="app.analysis.table.search-keyword" defaultMessage="Search keyword" />
<FormattedMessage id="BLOCK_NAME.table.search-keyword" defaultMessage="Search keyword" />
),
dataIndex: 'keyword',
key: 'keyword',
render: text => <a href="/">{text}</a>,
},
{
title: <FormattedMessage id="app.analysis.table.users" defaultMessage="Users" />,
title: <FormattedMessage id="BLOCK_NAME.table.users" defaultMessage="Users" />,
dataIndex: 'count',
key: 'count',
sorter: (a, b) => a.count - b.count,
className: styles.alignRight,
},
{
title: <FormattedMessage id="app.analysis.table.weekly-range" defaultMessage="Weekly Range" />,
title: <FormattedMessage id="BLOCK_NAME.table.weekly-range" defaultMessage="Weekly Range" />,
dataIndex: 'range',
key: 'range',
sorter: (a, b) => a.range - b.range,
......@@ -47,7 +47,7 @@ const TopSearch = memo(({ loading, visitData2, searchData, dropdownGroup }) => (
loading={loading}
bordered={false}
title={
<FormattedMessage id="app.analysis.online-top-search" defaultMessage="Online Top Search" />
<FormattedMessage id="BLOCK_NAME.analysis.online-top-search" defaultMessage="Online Top Search" />
}
extra={dropdownGroup}
style={{ marginTop: 24 }}
......@@ -57,9 +57,9 @@ const TopSearch = memo(({ loading, visitData2, searchData, dropdownGroup }) => (
<NumberInfo
subTitle={
<span>
<FormattedMessage id="app.analysis.search-users" defaultMessage="search users" />
<FormattedMessage id="BLOCK_NAME.analysis.search-users" defaultMessage="search users" />
<Tooltip
title={<FormattedMessage id="app.analysis.introduce" defaultMessage="introduce" />}
title={<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="introduce" />}
>
<Icon style={{ marginLeft: 8 }} type="info-circle-o" />
</Tooltip>
......@@ -77,11 +77,11 @@ const TopSearch = memo(({ loading, visitData2, searchData, dropdownGroup }) => (
subTitle={
<span>
<FormattedMessage
id="app.analysis.per-capita-search"
id="BLOCK_NAME.analysis.per-capita-search"
defaultMessage="Per Capita Search"
/>
<Tooltip
title={<FormattedMessage id="app.analysis.introduce" defaultMessage="introduce" />}
title={<FormattedMessage id="BLOCK_NAME.analysis.introduce" defaultMessage="introduce" />}
>
<Icon style={{ marginLeft: 8 }} type="info-circle-o" />
</Tooltip>
......
......@@ -2,23 +2,22 @@ import React, { Component, Suspense } from 'react';
import { connect } from 'dva';
import { Row, Col, Icon, Menu, Dropdown } from 'antd';
import GridContent from '@/components/PageHeaderWrapper/GridContent';
import { getTimeDistance } from '@/utils/utils';
import { getTimeDistance } from './utils/utils';
import styles from './Analysis.less';
import PageLoading from '@/components/PageLoading';
import styles from './style.less';
import PageLoading from './components/PageLoading';
const IntroduceRow = React.lazy(() => import('./IntroduceRow'));
const SalesCard = React.lazy(() => import('./SalesCard'));
const TopSearch = React.lazy(() => import('./TopSearch'));
const ProportionSales = React.lazy(() => import('./ProportionSales'));
const OfflineData = React.lazy(() => import('./OfflineData'));
const IntroduceRow = React.lazy(() => import('./components/IntroduceRow'));
const SalesCard = React.lazy(() => import('./components/SalesCard'));
const TopSearch = React.lazy(() => import('./components/TopSearch'));
const ProportionSales = React.lazy(() => import('./components/ProportionSales'));
const OfflineData = React.lazy(() => import('./components/OfflineData'));
@connect(({ chart, loading }) => ({
chart,
loading: loading.effects['chart/fetch'],
@connect(({ BLOCK_NAME_CAMEL_CASE, loading }) => ({
BLOCK_NAME_CAMEL_CASE,
loading: loading.effects['BLOCK_NAME_CAMEL_CASE/fetch'],
}))
class Analysis extends Component {
class PAGE_NAME_UPPER_CAMEL_CASE extends Component {
state = {
salesType: 'all',
currentTabKey: '',
......@@ -29,7 +28,7 @@ class Analysis extends Component {
const { dispatch } = this.props;
this.reqRef = requestAnimationFrame(() => {
dispatch({
type: 'chart/fetch',
type: 'BLOCK_NAME_CAMEL_CASE/fetch',
});
});
}
......@@ -37,7 +36,7 @@ class Analysis extends Component {
componentWillUnmount() {
const { dispatch } = this.props;
dispatch({
type: 'chart/clear',
type: 'BLOCK_NAME_CAMEL_CASE/clear',
});
cancelAnimationFrame(this.reqRef);
clearTimeout(this.timeoutId);
......@@ -62,7 +61,7 @@ class Analysis extends Component {
});
dispatch({
type: 'chart/fetchSalesData',
type: 'BLOCK_NAME_CAMEL_CASE/fetchSalesData',
});
};
......@@ -73,7 +72,7 @@ class Analysis extends Component {
});
dispatch({
type: 'chart/fetchSalesData',
type: 'BLOCK_NAME_CAMEL_CASE/fetchSalesData',
});
};
......@@ -94,7 +93,7 @@ class Analysis extends Component {
render() {
const { rangePickerValue, salesType, currentTabKey } = this.state;
const { chart, loading } = this.props;
const { BLOCK_NAME_CAMEL_CASE, loading } = this.props;
const {
visitData,
visitData2,
......@@ -105,7 +104,7 @@ class Analysis extends Component {
salesTypeData,
salesTypeDataOnline,
salesTypeDataOffline,
} = chart;
} = BLOCK_NAME_CAMEL_CASE;
let salesPieData;
if (salesType === 'all') {
salesPieData = salesTypeData;
......@@ -130,7 +129,7 @@ class Analysis extends Component {
const activeKey = currentTabKey || (offlineData[0] && offlineData[0].name);
return (
<GridContent>
<React.Fragment>
<Suspense fallback={<PageLoading />}>
<IntroduceRow loading={loading} visitData={visitData} />
</Suspense>
......@@ -144,32 +143,30 @@ class Analysis extends Component {
selectDate={this.selectDate}
/>
</Suspense>
<div className={styles.twoColLayout}>
<Row gutter={24}>
<Col xl={12} lg={24} md={24} sm={24} xs={24}>
<Suspense fallback={null}>
<TopSearch
loading={loading}
visitData2={visitData2}
selectDate={this.selectDate}
searchData={searchData}
dropdownGroup={dropdownGroup}
/>
</Suspense>
</Col>
<Col xl={12} lg={24} md={24} sm={24} xs={24}>
<Suspense fallback={null}>
<ProportionSales
dropdownGroup={dropdownGroup}
salesType={salesType}
loading={loading}
salesPieData={salesPieData}
handleChangeSalesType={this.handleChangeSalesType}
/>
</Suspense>
</Col>
</Row>
</div>
<Row gutter={24}>
<Col xl={12} lg={24} md={24} sm={24} xs={24}>
<Suspense fallback={null}>
<TopSearch
loading={loading}
visitData2={visitData2}
selectDate={this.selectDate}
searchData={searchData}
dropdownGroup={dropdownGroup}
/>
</Suspense>
</Col>
<Col xl={12} lg={24} md={24} sm={24} xs={24}>
<Suspense fallback={null}>
<ProportionSales
dropdownGroup={dropdownGroup}
salesType={salesType}
loading={loading}
salesPieData={salesPieData}
handleChangeSalesType={this.handleChangeSalesType}
/>
</Suspense>
</Col>
</Row>
<Suspense fallback={null}>
<OfflineData
activeKey={activeKey}
......@@ -179,9 +176,9 @@ class Analysis extends Component {
handleTabChange={this.handleTabChange}
/>
</Suspense>
</GridContent>
</React.Fragment>
);
}
}
export default Analysis;
export default PAGE_NAME_UPPER_CAMEL_CASE;
export default {
'BLOCK_NAME.analysis.test': 'Gongzhuan No.{no} shop',
'BLOCK_NAME.analysis.introduce': 'Introduce',
'BLOCK_NAME.analysis.total-sales': 'Total Sales',
'BLOCK_NAME.analysis.day-sales': 'Daily Sales',
'BLOCK_NAME.analysis.visits': 'Visits',
'BLOCK_NAME.analysis.visits-trend': 'Visits Trend',
'BLOCK_NAME.analysis.visits-ranking': 'Visits Ranking',
'BLOCK_NAME.analysis.day-visits': 'Daily Visits',
'BLOCK_NAME.analysis.week': 'WoW Change',
'BLOCK_NAME.analysis.day': 'DoD Change',
'BLOCK_NAME.analysis.payments': 'Payments',
'BLOCK_NAME.analysis.conversion-rate': 'Conversion Rate',
'BLOCK_NAME.analysis.operational-effect': 'Operational Effect',
'BLOCK_NAME.analysis.sales-trend': 'Stores Sales Trend',
'BLOCK_NAME.analysis.sales-ranking': 'Sales Ranking',
'BLOCK_NAME.analysis.all-year': 'All Year',
'BLOCK_NAME.analysis.all-month': 'All Month',
'BLOCK_NAME.analysis.all-week': 'All Week',
'BLOCK_NAME.analysis.all-day': 'All day',
'BLOCK_NAME.analysis.search-users': 'Search Users',
'BLOCK_NAME.analysis.per-capita-search': 'Per Capita Search',
'BLOCK_NAME.analysis.online-top-search': 'Online Top Search',
'BLOCK_NAME.analysis.the-proportion-of-sales': 'The Proportion Of Sales',
'BLOCK_NAME.channel.all': 'ALL',
'BLOCK_NAME.channel.online': 'Online',
'BLOCK_NAME.channel.stores': 'Stores',
'BLOCK_NAME.analysis.sales': 'Sales',
'BLOCK_NAME.analysis.traffic': 'Traffic',
'BLOCK_NAME.table.rank': 'Rank',
'BLOCK_NAME.table.search-keyword': 'Keyword',
'BLOCK_NAME.table.users': 'Users',
'BLOCK_NAME.table.weekly-range': 'Weekly Range',
};
export default {
'BLOCK_NAME.analysis.test': 'Gongzhuan No.{no} shop',
'BLOCK_NAME.analysis.introduce': 'Introduzir',
'BLOCK_NAME.analysis.total-sales': 'Vendas Totais',
'BLOCK_NAME.analysis.day-sales': 'Vendas do Dia',
'BLOCK_NAME.analysis.visits': 'Visitas',
'BLOCK_NAME.analysis.visits-trend': 'Tendência de Visitas',
'BLOCK_NAME.analysis.visits-ranking': 'Ranking de Visitas',
'BLOCK_NAME.analysis.day-visits': 'Visitas do Dia',
'BLOCK_NAME.analysis.week': 'Taxa Semanal',
'BLOCK_NAME.analysis.day': 'Taxa Diária',
'BLOCK_NAME.analysis.payments': 'Pagamentos',
'BLOCK_NAME.analysis.conversion-rate': 'Taxa de Conversão',
'BLOCK_NAME.analysis.operational-effect': 'Efeito Operacional',
'BLOCK_NAME.analysis.sales-trend': 'Tendência de Vendas das Lojas',
'BLOCK_NAME.analysis.sales-ranking': 'Ranking de Vendas',
'BLOCK_NAME.$2': 'Todo ano',
'BLOCK_NAME.analysis.all-month': 'Todo mês',
'BLOCK_NAME.analysis.all-week': 'Toda semana',
'BLOCK_NAME.analysis.all-day': 'Todo dia',
'BLOCK_NAME.analysis.search-users': 'Pesquisa de Usuários',
'BLOCK_NAME.analysis.per-capita-search': 'Busca Per Capta',
'BLOCK_NAME.analysis.online-top-search': 'Mais Buscadas Online',
'BLOCK_NAME.analysis.the-proportion-of-sales': 'The Proportion Of Sales',
'BLOCK_NAME.channel.all': 'Tudo',
'BLOCK_NAME.channel.online': 'Online',
'BLOCK_NAME.channel.stores': 'Lojas',
'BLOCK_NAME.analysis.sales': 'Vendas',
'BLOCK_NAME.analysis.traffic': 'Tráfego',
'BLOCK_NAME.table.rank': 'Rank',
'BLOCK_NAME.table.search-keyword': 'Palavra chave',
'BLOCK_NAME.table.users': 'Usuários',
'BLOCK_NAME.table.weekly-range': 'Faixa Semanal',
};
export default {
'BLOCK_NAME.analysis.test': '工专路 {no} 号店',
'BLOCK_NAME.analysis.introduce': '指标说明',
'BLOCK_NAME.analysis.total-sales': '总销售额',
'BLOCK_NAME.analysis.day-sales': '日销售额',
'BLOCK_NAME.analysis.visits': '访问量',
'BLOCK_NAME.analysis.visits-trend': '访问量趋势',
'BLOCK_NAME.analysis.visits-ranking': '门店访问量排名',
'BLOCK_NAME.analysis.day-visits': '日访问量',
'BLOCK_NAME.analysis.week': '周同比',
'BLOCK_NAME.analysis.day': '日同比',
'BLOCK_NAME.analysis.payments': '支付笔数',
'BLOCK_NAME.analysis.conversion-rate': '转化率',
'BLOCK_NAME.analysis.operational-effect': '运营活动效果',
'BLOCK_NAME.analysis.sales-trend': '销售趋势',
'BLOCK_NAME.analysis.sales-ranking': '门店销售额排名',
'BLOCK_NAME.analysis.all-year': '全年',
'BLOCK_NAME.analysis.all-month': '本月',
'BLOCK_NAME.analysis.all-week': '本周',
'BLOCK_NAME.analysis.all-day': '今日',
'BLOCK_NAME.analysis.search-users': '搜索用户数',
'BLOCK_NAME.analysis.per-capita-search': '人均搜索次数',
'BLOCK_NAME.analysis.online-top-search': '线上热门搜索',
'BLOCK_NAME.analysis.the-proportion-of-sales': '销售额类别占比',
'BLOCK_NAME.channel.all': '全部渠道',
'BLOCK_NAME.channel.online': '线上',
'BLOCK_NAME.channel.stores': '门店',
'BLOCK_NAME.analysis.sales': '销售额',
'BLOCK_NAME.analysis.traffic': '客流量',
'BLOCK_NAME.table.rank': '排名',
'BLOCK_NAME.table.search-keyword': '搜索关键词',
'BLOCK_NAME.table.users': '用户数',
'BLOCK_NAME.table.weekly-range': '周涨幅',
};
export default {
'BLOCK_NAME.analysis.test': '工專路 {no} 號店',
'BLOCK_NAME.analysis.introduce': '指標說明',
'BLOCK_NAME.analysis.total-sales': '總銷售額',
'BLOCK_NAME.analysis.day-sales': '日銷售額',
'BLOCK_NAME.analysis.visits': '訪問量',
'BLOCK_NAME.analysis.visits-trend': '訪問量趨勢',
'BLOCK_NAME.analysis.visits-ranking': '門店訪問量排名',
'BLOCK_NAME.analysis.day-visits': '日訪問量',
'BLOCK_NAME.analysis.week': '周同比',
'BLOCK_NAME.analysis.day': '日同比',
'BLOCK_NAME.analysis.payments': '支付筆數',
'BLOCK_NAME.analysis.conversion-rate': '轉化率',
'BLOCK_NAME.analysis.operational-effect': '運營活動效果',
'BLOCK_NAME.analysis.sales-trend': '銷售趨勢',
'BLOCK_NAME.analysis.sales-ranking': '門店銷售額排名',
'BLOCK_NAME.analysis.all-year': '全年',
'BLOCK_NAME.analysis.all-month': '本月',
'BLOCK_NAME.analysis.all-week': '本周',
'BLOCK_NAME.analysis.all-day': '今日',
'BLOCK_NAME.analysis.search-users': '搜索用戶數',
'BLOCK_NAME.analysis.per-capita-search': '人均搜索次數',
'BLOCK_NAME.analysis.online-top-search': '線上熱門搜索',
'BLOCK_NAME.analysis.the-proportion-of-sales': '銷售額類別占比',
'BLOCK_NAME.channel.all': '全部渠道',
'BLOCK_NAME.channel.online': '線上',
'BLOCK_NAME.channel.stores': '門店',
'BLOCK_NAME.analysis.sales': '銷售額',
'BLOCK_NAME.analysis.traffic': '客流量',
'BLOCK_NAME.table.rank': '排名',
'BLOCK_NAME.table.search-keyword': '搜索關鍵詞',
'BLOCK_NAME.table.users': '用戶數',
'BLOCK_NAME.table.weekly-range': '周漲幅',
};
import { fakeChartData } from '@/services/api';
import { fakeChartData } from './service';
export default {
namespace: 'chart',
namespace: 'BLOCK_NAME_CAMEL_CASE',
state: {
visitData: [],
......
import request from 'umi-request';
export async function fakeChartData() {
return request('/api/BLOCK_NAME/fake_chart_data');
}
@import '~antd/lib/style/themes/default.less';
@import '~@/utils/utils.less';
@import './utils/utils.less';
.iconGroup {
i {
......@@ -134,25 +134,6 @@
}
}
.twoColLayout {
.salesCard {
height: calc(100% - 24px);
}
div[class^='ant-col']:last-child {
right: 0\9;
height: 100%\9;
position: absolute\9;
}
:global {
.ant-row {
display: flex;
display: block\9;
flex-flow: row wrap;
position: relative\9;
}
}
}
.trendText {
margin-left: 8px;
color: @heading-color;
......
import React from 'react';
import { yuan } from '@/components/Charts';
import { yuan } from 'ant-design-pro/lib/Charts';
/**
* 减少使用 dangerouslySetInnerHTML
*/
......
import moment from 'moment';
export function fixedZero(val) {
return val * 1 < 10 ? `0${val}` : val;
}
export function getTimeDistance(type) {
const now = new Date();
const oneDay = 1000 * 60 * 60 * 24;
if (type === 'today') {
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
return [moment(now), moment(now.getTime() + (oneDay - 1000))];
}
if (type === 'week') {
let day = now.getDay();
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
if (day === 0) {
day = 6;
} else {
day -= 1;
}
const beginTime = now.getTime() - day * oneDay;
return [moment(beginTime), moment(beginTime + (7 * oneDay - 1000))];
}
if (type === 'month') {
const year = now.getFullYear();
const month = now.getMonth();
const nextDate = moment(now).add(1, 'months');
const nextYear = nextDate.year();
const nextMonth = nextDate.month();
return [
moment(`${year}-${fixedZero(month + 1)}-01 00:00:00`),
moment(moment(`${nextYear}-${fixedZero(nextMonth + 1)}-01 00:00:00`).valueOf() - 1000),
];
}
const year = now.getFullYear();
return [moment(`${year}-01-01 00:00:00`), moment(`${year}-12-31 23:59:59`)];
}
/yarn.lock
/package-lock.json
/dist
/node_modules
.umi
.umi-production
export default {
plugins: [
['umi-plugin-block-dev', {
layout: 'ant-design-pro',
}],
['umi-plugin-react', {
dva: true,
locale: true,
antd: true,
}]
],
}
# @umi-blocks/ant-design-pro/basicform
BasicForm
## Usage
```sh
umi block add ant-design-pro/basicform
```
## SNAPSHOT
![SNAPSHOT](./snapshot.png)
## LICENSE
MIT
{
"name": "@umi-block/basic-form",
"version": "0.0.1",
"description": "BasicForm",
"main": "src/index.js",
"scripts": {
"dev": "umi dev"
},
"repository": {
"type": "git",
"url": "https://github.com/umijs/umi-blocks/ant-design-pro/basicform"
},
"dependencies": {
"ant-design-pro": "^2.1.1",
"antd": "^3.10.9",
"dva": "^2.4.0",
"react": "^16.6.3",
"umi-request": "^1.0.0"
},
"devDependencies": {
"umi": "^2.3.0-beta.1",
"umi-plugin-react": "^1.3.0-beta.1",
"umi-plugin-block-dev": "^1.0.0"
},
"license": "ISC"
}
export default {
'POST /api/BLOCK_NAME/forms': (req, res) => {
res.send({ message: 'Ok' });
},
};
import React from 'react';
import { FormattedMessage } from 'umi/locale';
import Link from 'umi/link';
import { PageHeader } from 'ant-design-pro';
import styles from './index.less';
const PageHeaderWrapper = ({ children, wrapperClassName, ...restProps }) => (
<div style={{ margin: '-24px -24px 0' }} className={wrapperClassName}>
<PageHeader
home={<FormattedMessage id="BLOCK_NAME.menu.home" defaultMessage="Home" />}
key="pageheader"
{...restProps}
linkElement={Link}
itemRender={item => {
if (item.locale) {
return <FormattedMessage id={item.locale} defaultMessage={item.title} />;
}
return item.title;
}}
/>
{children ? <div className={styles.content}>{children}</div> : null}
</div>
);
export default PageHeaderWrapper;
@import '~antd/lib/style/themes/default.less';
.content {
margin: 24px 24px 0;
}
@media screen and (max-width: @screen-sm) {
.content {
margin: 24px 0 0;
}
}
......@@ -13,7 +13,7 @@ import {
Icon,
Tooltip,
} from 'antd';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import PageHeaderWrapper from './components/PageHeaderWrapper';
import styles from './style.less';
const FormItem = Form.Item;
......@@ -22,17 +22,17 @@ const { RangePicker } = DatePicker;
const { TextArea } = Input;
@connect(({ loading }) => ({
submitting: loading.effects['form/submitRegularForm'],
submitting: loading.effects['BLOCK_NAME_CAMEL_CASE/submitRegularForm'],
}))
@Form.create()
class BasicForms extends PureComponent {
class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
handleSubmit = e => {
const { dispatch, form } = this.props;
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (!err) {
dispatch({
type: 'form/submitRegularForm',
type: 'BLOCK_NAME_CAMEL_CASE/submitRegularForm',
payload: values,
});
}
......@@ -66,67 +66,67 @@ class BasicForms extends PureComponent {
return (
<PageHeaderWrapper
title={<FormattedMessage id="app.forms.basic.title" />}
content={<FormattedMessage id="app.forms.basic.description" />}
title={<FormattedMessage id="BLOCK_NAME.basic.title" />}
content={<FormattedMessage id="BLOCK_NAME.basic.description" />}
>
<Card bordered={false}>
<Form onSubmit={this.handleSubmit} hideRequiredMark style={{ marginTop: 8 }}>
<FormItem {...formItemLayout} label={<FormattedMessage id="form.title.label" />}>
<FormItem {...formItemLayout} label={<FormattedMessage id="BLOCK_NAME.title.label" />}>
{getFieldDecorator('title', {
rules: [
{
required: true,
message: formatMessage({ id: 'validation.title.required' }),
message: formatMessage({ id: 'BLOCK_NAME.title.required' }),
},
],
})(<Input placeholder={formatMessage({ id: 'form.title.placeholder' })} />)}
})(<Input placeholder={formatMessage({ id: 'BLOCK_NAME.title.placeholder' })} />)}
</FormItem>
<FormItem {...formItemLayout} label={<FormattedMessage id="form.date.label" />}>
<FormItem {...formItemLayout} label={<FormattedMessage id="BLOCK_NAME.date.label" />}>
{getFieldDecorator('date', {
rules: [
{
required: true,
message: formatMessage({ id: 'validation.date.required' }),
message: formatMessage({ id: 'BLOCK_NAME.date.required' }),
},
],
})(
<RangePicker
style={{ width: '100%' }}
placeholder={[
formatMessage({ id: 'form.date.placeholder.start' }),
formatMessage({ id: 'form.date.placeholder.end' }),
formatMessage({ id: 'BLOCK_NAME.placeholder.start' }),
formatMessage({ id: 'BLOCK_NAME.placeholder.end' }),
]}
/>
)}
</FormItem>
<FormItem {...formItemLayout} label={<FormattedMessage id="form.goal.label" />}>
<FormItem {...formItemLayout} label={<FormattedMessage id="BLOCK_NAME.goal.label" />}>
{getFieldDecorator('goal', {
rules: [
{
required: true,
message: formatMessage({ id: 'validation.goal.required' }),
message: formatMessage({ id: 'BLOCK_NAME.goal.required' }),
},
],
})(
<TextArea
style={{ minHeight: 32 }}
placeholder={formatMessage({ id: 'form.goal.placeholder' })}
placeholder={formatMessage({ id: 'BLOCK_NAME.goal.placeholder' })}
rows={4}
/>
)}
</FormItem>
<FormItem {...formItemLayout} label={<FormattedMessage id="form.standard.label" />}>
<FormItem {...formItemLayout} label={<FormattedMessage id="BLOCK_NAME.standard.label" />}>
{getFieldDecorator('standard', {
rules: [
{
required: true,
message: formatMessage({ id: 'validation.standard.required' }),
message: formatMessage({ id: 'BLOCK_NAME.standard.required' }),
},
],
})(
<TextArea
style={{ minHeight: 32 }}
placeholder={formatMessage({ id: 'form.standard.placeholder' })}
placeholder={formatMessage({ id: 'BLOCK_NAME.standard.placeholder' })}
rows={4}
/>
)}
......@@ -135,10 +135,10 @@ class BasicForms extends PureComponent {
{...formItemLayout}
label={
<span>
<FormattedMessage id="form.client.label" />
<FormattedMessage id="BLOCK_NAME.client.label" />
<em className={styles.optional}>
<FormattedMessage id="form.optional" />
<Tooltip title={<FormattedMessage id="form.client.label.tooltip" />}>
<FormattedMessage id="BLOCK_NAME.form.optional" />
<Tooltip title={<FormattedMessage id="BLOCK_NAME.label.tooltip" />}>
<Icon type="info-circle-o" style={{ marginRight: 4 }} />
</Tooltip>
</em>
......@@ -146,38 +146,38 @@ class BasicForms extends PureComponent {
}
>
{getFieldDecorator('client')(
<Input placeholder={formatMessage({ id: 'form.client.placeholder' })} />
<Input placeholder={formatMessage({ id: 'BLOCK_NAME.client.placeholder' })} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label={
<span>
<FormattedMessage id="form.invites.label" />
<FormattedMessage id="BLOCK_NAME.invites.label" />
<em className={styles.optional}>
<FormattedMessage id="form.optional" />
<FormattedMessage id="BLOCK_NAME.form.optional" />
</em>
</span>
}
>
{getFieldDecorator('invites')(
<Input placeholder={formatMessage({ id: 'form.invites.placeholder' })} />
<Input placeholder={formatMessage({ id: 'BLOCK_NAME.invites.placeholder' })} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label={
<span>
<FormattedMessage id="form.weight.label" />
<FormattedMessage id="BLOCK_NAME.weight.label" />
<em className={styles.optional}>
<FormattedMessage id="form.optional" />
<FormattedMessage id="BLOCK_NAME.form.optional" />
</em>
</span>
}
>
{getFieldDecorator('weight')(
<InputNumber
placeholder={formatMessage({ id: 'form.weight.placeholder' })}
placeholder={formatMessage({ id: 'BLOCK_NAME.weight.placeholder' })}
min={0}
max={100}
/>
......@@ -186,8 +186,8 @@ class BasicForms extends PureComponent {
</FormItem>
<FormItem
{...formItemLayout}
label={<FormattedMessage id="form.public.label" />}
help={<FormattedMessage id="form.public.label.help" />}
label={<FormattedMessage id="BLOCK_NAME.public.label" />}
help={<FormattedMessage id="BLOCK_NAME.label.help" />}
>
<div>
{getFieldDecorator('public', {
......@@ -195,13 +195,13 @@ class BasicForms extends PureComponent {
})(
<Radio.Group>
<Radio value="1">
<FormattedMessage id="form.public.radio.public" />
<FormattedMessage id="BLOCK_NAME.radio.public" />
</Radio>
<Radio value="2">
<FormattedMessage id="form.public.radio.partially-public" />
<FormattedMessage id="BLOCK_NAME.radio.partially-public" />
</Radio>
<Radio value="3">
<FormattedMessage id="form.public.radio.private" />
<FormattedMessage id="BLOCK_NAME.radio.private" />
</Radio>
</Radio.Group>
)}
......@@ -209,20 +209,20 @@ class BasicForms extends PureComponent {
{getFieldDecorator('publicUsers')(
<Select
mode="multiple"
placeholder={formatMessage({ id: 'form.publicUsers.placeholder' })}
placeholder={formatMessage({ id: 'BLOCK_NAME.publicUsers.placeholder' })}
style={{
margin: '8px 0',
display: getFieldValue('public') === '2' ? 'block' : 'none',
}}
>
<Option value="1">
<FormattedMessage id="form.publicUsers.option.A" />
<FormattedMessage id="BLOCK_NAME.option.A" />
</Option>
<Option value="2">
<FormattedMessage id="form.publicUsers.option.B" />
<FormattedMessage id="BLOCK_NAME.option.B" />
</Option>
<Option value="3">
<FormattedMessage id="form.publicUsers.option.C" />
<FormattedMessage id="BLOCK_NAME.option.C" />
</Option>
</Select>
)}
......@@ -231,10 +231,10 @@ class BasicForms extends PureComponent {
</FormItem>
<FormItem {...submitFormLayout} style={{ marginTop: 32 }}>
<Button type="primary" htmlType="submit" loading={submitting}>
<FormattedMessage id="form.submit" />
<FormattedMessage id="BLOCK_NAME.form.submit" />
</Button>
<Button style={{ marginLeft: 8 }}>
<FormattedMessage id="form.save" />
<FormattedMessage id="BLOCK_NAME.form.save" />
</Button>
</FormItem>
</Form>
......@@ -244,4 +244,4 @@ class BasicForms extends PureComponent {
}
}
export default BasicForms;
export default PAGE_NAME_UPPER_CAMEL_CASE;
export default {
'BLOCK_NAME.basic.title': 'Basic form',
'BLOCK_NAME.basic.description':
'Form pages are used to collect or verify information to users, and basic forms are common in scenarios where there are fewer data items.',
'BLOCK_NAME.email.required': 'Please enter your email!',
'BLOCK_NAME.email.wrong-format': 'The email address is in the wrong format!',
'BLOCK_NAME.userName.required': 'Please enter your userName!',
'BLOCK_NAME.password.required': 'Please enter your password!',
'BLOCK_NAME.password.twice': 'The passwords entered twice do not match!',
'BLOCK_NAME.strength.msg':
"Please enter at least 6 characters and don't use passwords that are easy to guess.",
'BLOCK_NAME.strength.strong': 'Strength: strong',
'BLOCK_NAME.strength.medium': 'Strength: medium',
'BLOCK_NAME.strength.short': 'Strength: too short',
'BLOCK_NAME.confirm-password.required': 'Please confirm your password!',
'BLOCK_NAME.phone-number.required': 'Please enter your phone number!',
'BLOCK_NAME.phone-number.wrong-format': 'Malformed phone number!',
'BLOCK_NAME.verification-code.required': 'Please enter the verification code!',
'BLOCK_NAME.title.required': 'Please enter a title',
'BLOCK_NAME.date.required': 'Please select the start and end date',
'BLOCK_NAME.goal.required': 'Please enter a description of the goal',
'BLOCK_NAME.standard.required': 'Please enter a metric',
'BLOCK_NAME.form.get-captcha': 'Get Captcha',
'BLOCK_NAME.captcha.second': 'sec',
'BLOCK_NAME.form.optional': ' (optional) ',
'BLOCK_NAME.form.submit': 'Submit',
'BLOCK_NAME.form.save': 'Save',
'BLOCK_NAME.email.placeholder': 'Email',
'BLOCK_NAME.password.placeholder': 'Password',
'BLOCK_NAME.confirm-password.placeholder': 'Confirm password',
'BLOCK_NAME.phone-number.placeholder': 'Phone number',
'BLOCK_NAME.verification-code.placeholder': 'Verification code',
'BLOCK_NAME.title.label': 'Title',
'BLOCK_NAME.title.placeholder': 'Give the target a name',
'BLOCK_NAME.date.label': 'Start and end date',
'BLOCK_NAME.placeholder.start': 'Start date',
'BLOCK_NAME.placeholder.end': 'End date',
'BLOCK_NAME.goal.label': 'Goal description',
'BLOCK_NAME.goal.placeholder': 'Please enter your work goals',
'BLOCK_NAME.standard.label': 'Metrics',
'BLOCK_NAME.standard.placeholder': 'Please enter a metric',
'BLOCK_NAME.client.label': 'Client',
'BLOCK_NAME.label.tooltip': 'Target service object',
'BLOCK_NAME.client.placeholder':
'Please describe your customer service, internal customers directly @ Name / job number',
'BLOCK_NAME.invites.label': 'Inviting critics',
'BLOCK_NAME.invites.placeholder': 'Please direct @ Name / job number, you can invite up to 5 people',
'BLOCK_NAME.weight.label': 'Weight',
'BLOCK_NAME.weight.placeholder': 'Please enter weight',
'BLOCK_NAME.public.label': 'Target disclosure',
'BLOCK_NAME.label.help': 'Customers and invitees are shared by default',
'BLOCK_NAME.radio.public': 'Public',
'BLOCK_NAME.radio.partially-public': 'Partially public',
'BLOCK_NAME.radio.private': 'Private',
'BLOCK_NAME.publicUsers.placeholder': 'Open to',
'BLOCK_NAME.option.A': 'Colleague A',
'BLOCK_NAME.option.B': 'Colleague B',
'BLOCK_NAME.option.C': 'Colleague C',
};
export default {
'BLOCK_NAME.basic.title': 'Basic form',
'BLOCK_NAME.basic.description':
'Form pages are used to collect or verify information to users, and basic forms are common in scenarios where there are fewer data items.',
'BLOCK_NAME.email.required': 'Por favor insira seu email!',
'BLOCK_NAME.email.wrong-format': 'O email está errado!',
'BLOCK_NAME.userName.required': 'Por favor insira nome de usuário!',
'BLOCK_NAME.password.required': 'Por favor insira sua senha!',
'BLOCK_NAME.password.twice': 'As senhas não estão iguais!',
'BLOCK_NAME.strength.msg':
'Por favor insira pelo menos 6 caracteres e não use senhas fáceis de adivinhar.',
'BLOCK_NAME.strength.strong': 'Força: forte',
'BLOCK_NAME.strength.medium': 'Força: média',
'BLOCK_NAME.strength.short': 'Força: curta',
'BLOCK_NAME.confirm-password.required': 'Por favor confirme sua senha!',
'BLOCK_NAME.phone-number.required': 'Por favor insira seu telefone!',
'BLOCK_NAME.phone-number.wrong-format': 'Formato de telefone errado!',
'BLOCK_NAME.verification-code.required': 'Por favor insira seu código de verificação!',
'BLOCK_NAME.form.get-captcha': 'Get Captcha',
'BLOCK_NAME.captcha.second': 'sec',
'BLOCK_NAME.email.placeholder': 'Email',
'BLOCK_NAME.password.placeholder': 'Senha',
'BLOCK_NAME.confirm-password.placeholder': 'Confirme a senha',
'BLOCK_NAME.phone-number.placeholder': 'Telefone',
'BLOCK_NAME.verification-code.placeholder': 'Código de verificação',
'BLOCK_NAME.form.optional': ' (optional) ',
'BLOCK_NAME.form.submit': 'Submit',
'BLOCK_NAME.form.save': 'Save',
'BLOCK_NAME.title.label': 'Title',
'BLOCK_NAME.title.placeholder': 'Give the target a name',
'BLOCK_NAME.date.label': 'Start and end date',
'BLOCK_NAME.placeholder.start': 'Start date',
'BLOCK_NAME.placeholder.end': 'End date',
'BLOCK_NAME.goal.label': 'Goal description',
'BLOCK_NAME.goal.placeholder': 'Please enter your work goals',
'BLOCK_NAME.standard.label': 'Metrics',
'BLOCK_NAME.standard.placeholder': 'Please enter a metric',
'BLOCK_NAME.client.label': 'Client',
'BLOCK_NAME.label.tooltip': 'Target service object',
'BLOCK_NAME.client.placeholder':
'Please describe your customer service, internal customers directly @ Name / job number',
'BLOCK_NAME.invites.label': 'Inviting critics',
'BLOCK_NAME.invites.placeholder': 'Please direct @ Name / job number, you can invite up to 5 people',
'BLOCK_NAME.weight.label': 'Weight',
'BLOCK_NAME.weight.placeholder': 'Please enter weight',
'BLOCK_NAME.public.label': 'Target disclosure',
'BLOCK_NAME.label.help': 'Customers and invitees are shared by default',
'BLOCK_NAME.radio.public': 'Public',
'BLOCK_NAME.radio.partially-public': 'Partially public',
'BLOCK_NAME.radio.private': 'Private',
'BLOCK_NAME.publicUsers.placeholder': 'Open to',
'BLOCK_NAME.option.A': 'Colleague A',
'BLOCK_NAME.option.B': 'Colleague B',
'BLOCK_NAME.option.C': 'Colleague C',
};
export default {
'BLOCK_NAME.basic.title': '基础表单',
'BLOCK_NAME.basic.description':
'表单页用于向用户收集或验证信息,基础表单常见于数据项较少的表单场景。',
'BLOCK_NAME.email.required': '请输入邮箱地址!',
'BLOCK_NAME.email.wrong-format': '邮箱地址格式错误!',
'BLOCK_NAME.userName.required': '请输入用户名!',
'BLOCK_NAME.password.required': '请输入密码!',
'BLOCK_NAME.password.twice': '两次输入的密码不匹配!',
'BLOCK_NAME.strength.msg': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',
'BLOCK_NAME.strength.strong': '强度:强',
'BLOCK_NAME.strength.medium': '强度:中',
'BLOCK_NAME.strength.short': '强度:太短',
'BLOCK_NAME.confirm-password.required': '请确认密码!',
'BLOCK_NAME.phone-number.required': '请输入手机号!',
'BLOCK_NAME.phone-number.wrong-format': '手机号格式错误!',
'BLOCK_NAME.verification-code.required': '请输入验证码!',
'BLOCK_NAME.title.required': '请输入标题',
'BLOCK_NAME.date.required': '请选择起止日期',
'BLOCK_NAME.goal.required': '请输入目标描述',
'BLOCK_NAME.standard.required': '请输入衡量标准',
'BLOCK_NAME.form.get-captcha': '获取验证码',
'BLOCK_NAME.captcha.second': '',
'BLOCK_NAME.form.optional': '(选填)',
'BLOCK_NAME.form.submit': '提交',
'BLOCK_NAME.form.save': '保存',
'BLOCK_NAME.email.placeholder': '邮箱',
'BLOCK_NAME.password.placeholder': '至少6位密码,区分大小写',
'BLOCK_NAME.confirm-password.placeholder': '确认密码',
'BLOCK_NAME.phone-number.placeholder': '手机号',
'BLOCK_NAME.verification-code.placeholder': '验证码',
'BLOCK_NAME.title.label': '标题',
'BLOCK_NAME.title.placeholder': '给目标起个名字',
'BLOCK_NAME.date.label': '起止日期',
'BLOCK_NAME.placeholder.start': '开始日期',
'BLOCK_NAME.placeholder.end': '结束日期',
'BLOCK_NAME.goal.label': '目标描述',
'BLOCK_NAME.goal.placeholder': '请输入你的阶段性工作目标',
'BLOCK_NAME.standard.label': '衡量标准',
'BLOCK_NAME.standard.placeholder': '请输入衡量标准',
'BLOCK_NAME.client.label': '客户',
'BLOCK_NAME.label.tooltip': '目标的服务对象',
'BLOCK_NAME.client.placeholder': '请描述你服务的客户,内部客户直接 @姓名/工号',
'BLOCK_NAME.invites.label': '邀评人',
'BLOCK_NAME.invites.placeholder': '请直接 @姓名/工号,最多可邀请 5 人',
'BLOCK_NAME.weight.label': '权重',
'BLOCK_NAME.weight.placeholder': '请输入',
'BLOCK_NAME.public.label': '目标公开',
'BLOCK_NAME.label.help': '客户、邀评人默认被分享',
'BLOCK_NAME.radio.public': '公开',
'BLOCK_NAME.radio.partially-public': '部分公开',
'BLOCK_NAME.radio.private': '不公开',
'BLOCK_NAME.publicUsers.placeholder': '公开给',
'BLOCK_NAME.option.A': '同事甲',
'BLOCK_NAME.option.B': '同事乙',
'BLOCK_NAME.option.C': '同事丙',
};
export default {
'BLOCK_NAME.basic.title': '基礎表單',
'BLOCK_NAME.basic.description':
'表單頁用於向用戶收集或驗證信息,基礎表單常見於數據項較少的表單場景。',
'BLOCK_NAME.email.required': '請輸入郵箱地址!',
'BLOCK_NAME.email.wrong-format': '郵箱地址格式錯誤!',
'BLOCK_NAME.userName.required': '請輸入賬戶!',
'BLOCK_NAME.password.required': '請輸入密碼!',
'BLOCK_NAME.password.twice': '兩次輸入的密碼不匹配!',
'BLOCK_NAME.strength.msg': '請至少輸入 6 個字符。請不要使用容易被猜到的密碼。',
'BLOCK_NAME.strength.strong': '強度:強',
'BLOCK_NAME.strength.medium': '強度:中',
'BLOCK_NAME.strength.short': '強度:太短',
'BLOCK_NAME.confirm-password.required': '請確認密碼!',
'BLOCK_NAME.phone-number.required': '請輸入手機號!',
'BLOCK_NAME.phone-number.wrong-format': '手機號格式錯誤!',
'BLOCK_NAME.verification-code.required': '請輸入驗證碼!',
'BLOCK_NAME.title.required': '請輸入標題',
'BLOCK_NAME.date.required': '請選擇起止日期',
'BLOCK_NAME.goal.required': '請輸入目標描述',
'BLOCK_NAME.standard.required': '請輸入衡量標淮',
'BLOCK_NAME.form.get-captcha': '獲取驗證碼',
'BLOCK_NAME.captcha.second': '',
'BLOCK_NAME.form.optional': '(選填)',
'BLOCK_NAME.form.submit': '提交',
'BLOCK_NAME.form.save': '保存',
'BLOCK_NAME.email.placeholder': '郵箱',
'BLOCK_NAME.password.placeholder': '至少6位密碼,區分大小寫',
'BLOCK_NAME.confirm-password.placeholder': '確認密碼',
'BLOCK_NAME.phone-number.placeholder': '手機號',
'BLOCK_NAME.verification-code.placeholder': '驗證碼',
'BLOCK_NAME.title.label': '標題',
'BLOCK_NAME.title.placeholder': '給目標起個名字',
'BLOCK_NAME.date.label': '起止日期',
'BLOCK_NAME.placeholder.start': '開始日期',
'BLOCK_NAME.placeholder.end': '結束日期',
'BLOCK_NAME.goal.label': '目標描述',
'BLOCK_NAME.goal.placeholder': '請輸入妳的階段性工作目標',
'BLOCK_NAME.standard.label': '衡量標淮',
'BLOCK_NAME.standard.placeholder': '請輸入衡量標淮',
'BLOCK_NAME.client.label': '客戶',
'BLOCK_NAME.label.tooltip': '目標的服務對象',
'BLOCK_NAME.client.placeholder': '請描述妳服務的客戶,內部客戶直接 @姓名/工號',
'BLOCK_NAME.invites.label': '邀評人',
'BLOCK_NAME.invites.placeholder': '請直接 @姓名/工號,最多可邀請 5 人',
'BLOCK_NAME.weight.label': '權重',
'BLOCK_NAME.weight.placeholder': '請輸入',
'BLOCK_NAME.public.label': '目標公開',
'BLOCK_NAME.label.help': '客戶、邀評人默認被分享',
'BLOCK_NAME.radio.public': '公開',
'BLOCK_NAME.radio.partially-public': '部分公開',
'BLOCK_NAME.radio.private': '不公開',
'BLOCK_NAME.publicUsers.placeholder': '公開給',
'BLOCK_NAME.option.A': '同事甲',
'BLOCK_NAME.option.B': '同事乙',
'BLOCK_NAME.option.C': '同事丙',
};
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment