Unverified Commit c08aa5ba authored by 陈帅's avatar 陈帅 Committed by GitHub

Merge pull request #14 from ant-design/feat/workplacetots

feat: Workplace migrate to TypeScript
parents 40ac8b52 3b803cb7
......@@ -3,7 +3,7 @@ export default {
[
'umi-plugin-block-dev',
{
layout: 'ant-design-pro-user',
layout: 'ant-design-pro',
menu: {
name: '主页',
icon: 'home',
......
......@@ -3,7 +3,6 @@ import ChartCard from './ChartCard';
import Field from './Field';
import Bar from './Bar';
import Pie from './Pie';
import Radar from './Radar';
import Gauge from './Gauge';
import MiniArea from './MiniArea';
import MiniBar from './MiniBar';
......@@ -19,7 +18,6 @@ const Charts = {
Bar,
Pie,
Gauge,
Radar,
MiniBar,
MiniArea,
MiniProgress,
......@@ -36,7 +34,6 @@ export {
Bar,
Pie,
Gauge,
Radar,
MiniBar,
MiniArea,
MiniProgress,
......
......@@ -16,7 +16,8 @@
"moment": "^2.22.2",
"prop-types": "^15.5.10",
"react": "^16.6.3",
"umi-request": "^1.0.0"
"umi-request": "^1.0.0",
"bizcharts": "^3.5.2"
},
"devDependencies": {
"umi": "^2.6.9",
......@@ -24,4 +25,4 @@
"umi-plugin-react": "^1.3.0-beta.1"
},
"license": "MIT"
}
\ No newline at end of file
}
import React, { PureComponent, createElement } from 'react';
import PropTypes from 'prop-types';
import PropTypes, { string } from 'prop-types';
import { Button } from 'antd';
import styles from './index.less';
// TODO: 添加逻辑
export interface EditableLink {
title: string;
href: string;
}
interface EditableLinkGroupProps {
onAdd: () => void;
links: EditableLink[];
linkElement: React.Component;
}
class EditableLinkGroup extends PureComponent {
class EditableLinkGroup extends PureComponent<EditableLinkGroupProps> {
static propTypes = {
links: PropTypes.array,
onAdd: PropTypes.func,
......
import React from 'react';
import { FormattedMessage } from 'umi-plugin-react/locale';
import Link from 'umi/link';
import { PageHeader } from 'ant-design-pro';
import styles from './index.less';
const PageHeaderWrapper = ({ children, contentWidth, wrapperClassName, ...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 {
.children-content {
margin: 24px 24px 0;
}
@media screen and (max-width: @screen-sm) {
.main {
.detail {
display: flex;
}
.row {
display: flex;
width: 100%;
}
.title-content {
margin-bottom: 16px;
}
@media screen and (max-width: @screen-sm) {
.content {
margin: 24px 0 0;
}
}
.title,
.content {
margin: 24px 0 0;
flex: auto;
}
.extraContent,
.main {
flex: 0 1 auto;
}
.main {
width: 100%;
}
.title {
margin-bottom: 16px;
}
.logo,
.content,
.extraContent {
margin-bottom: 16px;
}
.extraContent {
min-width: 242px;
margin-left: 88px;
text-align: right;
}
}
@media screen and (max-width: @screen-xl) {
.extraContent {
margin-left: 44px;
}
}
@media screen and (max-width: @screen-lg) {
.extraContent {
margin-left: 20px;
}
}
@media screen and (max-width: @screen-md) {
.row {
display: block;
}
.action,
.extraContent {
margin-left: 0;
text-align: left;
}
}
@media screen and (max-width: @screen-sm) {
.detail {
display: block;
}
}
import React from 'react';
import { RouteContext } from '@ant-design/pro-layout';
import { PageHeader, Typography } from 'antd';
import styles from './index.less';
import { GridContent } from '@ant-design/pro-layout';
interface IPageHeaderWrapperProps {
content?: React.ReactNode;
title?: React.ReactNode;
extraContent?: React.ReactNode;
}
const PageHeaderWrapper: React.SFC<IPageHeaderWrapperProps> = ({
children,
content,
title,
extraContent,
...restProps
}) => (
<RouteContext.Consumer>
{value => (
<div style={{ margin: '-24px -24px 0' }}>
<PageHeader
title={title}
{...restProps}
{...value}
>
<div className={styles.detail}>
<div className={styles.main}>
<div className={styles.row}>
{content && <div className={styles.content}>{content}</div>}
{extraContent && <div className={styles.extraContent}>{extraContent}</div>}
</div>
</div>
</div>
</PageHeader>
{children ? (
<GridContent>
<div className={styles['children-content']}>{children}</div>
</GridContent>
) : null}
</div>
)}
</RouteContext.Consumer>
);
export default PageHeaderWrapper;
import React from 'react';
export type IReactComponent<P = any> =
| React.StatelessComponent<P>
| React.ComponentClass<P>
| React.ClassicComponentClass<P>;
function computeHeight(node: HTMLDivElement) {
node.style.height = '100%';
const totalHeight = parseInt(getComputedStyle(node).height + '', 10);
const padding =
parseInt(getComputedStyle(node).paddingTop + '', 10) +
parseInt(getComputedStyle(node).paddingBottom + '', 10);
return totalHeight - padding;
}
function getAutoHeight(n: HTMLDivElement) {
if (!n) {
return 0;
}
let node = n;
let height = computeHeight(node);
const parentNode = node.parentNode as HTMLDivElement;
if (parentNode) {
height = computeHeight(parentNode);
}
return height;
}
interface IAutoHeightProps {
height?: number;
}
function autoHeight() {
return function<P extends IAutoHeightProps>(
WrappedComponent: React.ComponentClass<P> | React.SFC<P>
): React.ComponentClass<P> {
class AutoHeightComponent extends React.Component<P & IAutoHeightProps> {
state = {
computedHeight: 0,
};
root!: HTMLDivElement;
componentDidMount() {
const { height } = this.props;
if (!height) {
const h = getAutoHeight(this.root);
// eslint-disable-next-line
this.setState({ computedHeight: h });
if (h < 1) {
const h = getAutoHeight(this.root);
this.setState({ computedHeight: h });
}
}
}
handleRoot = (node: HTMLDivElement) => {
this.root = node;
};
render() {
const { height } = this.props;
const { computedHeight } = this.state;
const h = height || computedHeight;
return (
<div ref={this.handleRoot}>
{h > 0 && <WrappedComponent {...this.props} height={h} />}
</div>
);
}
}
return AutoHeightComponent;
};
}
export default autoHeight;
import React, { Component } from 'react';
import { Chart, G2, Tooltip, Geom, Coord, Axis } from 'bizcharts';
import { Chart, Tooltip, Geom, Coord, Axis } from 'bizcharts';
import { Row, Col } from 'antd';
import autoHeight from '../autoHeight';
import autoHeight from './autoHeight';
import styles from './index.less';
export interface IRadarProps {
......
export interface ITag {
key: string;
label: string;
}
export interface Province {
label: string;
key: string;
}
export interface City {
label: string;
key: string;
}
export interface Geographic {
province: Province;
city: City;
}
export interface Notice {
id: string;
title: string;
logo: string;
description: string;
updatedAt: string;
member: string;
href: string;
memberLink: string;
}
export interface CurrentUser {
name: string;
avatar: string;
userid: string;
notice: Notice[];
email: string;
signature: string;
title: string;
group: string;
tags: ITag[];
notifyCount: number;
unreadCount: number;
country: string;
geographic: Geographic;
address: string;
phone: string;
}
export interface Member {
avatar: string;
name: string;
id: string;
}
export interface Activeties {
id: string,
updatedAt: string,
user: {
name: string,
avatar: string,
},
group: {
name: string,
link: string,
},
project: {
name: string,
link: string,
},
template: string,
}
export interface RadarData {
label: string;
name: string;
value: number;
}
......@@ -3,15 +3,17 @@ import moment from 'moment';
import { connect } from 'dva';
import Link from 'umi/link';
import { Row, Col, Card, List, Avatar } from 'antd';
import { Dispatch } from 'redux';
import { Charts } from 'ant-design-pro';
import EditableLinkGroup from './components/EditableLinkGroup';
import PageHeaderWrapper from './components/PageHeaderWrapper';
import Radar from './components/Radar';
import { ModalState } from './model';
import { CurrentUser, Activeties, RadarData, Notice } from './data';
import styles from './style.less';
const { Radar } = Charts;
const links = [
{
title: '操作一',
......@@ -39,16 +41,33 @@ const links = [
},
];
@connect(({ BLOCK_NAME_CAMEL_CASE: { user, project, activities, chart }, loading }) => ({
currentUser: user.currentUser,
project,
interface BLOCK_NAME_CAMEL_CASEProps {
currentUser: CurrentUser;
projectNotice: Notice[];
activities: Activeties[];
radarData: RadarData[];
dispatch: Dispatch;
currentUserLoading: boolean;
projectLoading: boolean;
activitiesLoading: boolean;
}
@connect(({
BLOCK_NAME_CAMEL_CASE: { currentUser, projectNotice, activities, radarData },
loading,
}: {
BLOCK_NAME_CAMEL_CASE: ModalState,
loading: { effects: any },
}) => ({
currentUser,
projectNotice,
activities,
chart,
radarData,
currentUserLoading: loading.effects['BLOCK_NAME_CAMEL_CASE/fetchUserCurrent'],
projectLoading: loading.effects['BLOCK_NAME_CAMEL_CASE/fetchProjectNotice'],
activitiesLoading: loading.effects['BLOCK_NAME_CAMEL_CASE/fetchActivitiesList'],
}))
class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<BLOCK_NAME_CAMEL_CASEProps> {
componentDidMount() {
const { dispatch } = this.props;
dispatch({
......@@ -65,9 +84,9 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
renderActivities() {
const {
activities: { list },
activities,
} = this.props;
return list.map(item => {
return activities.map(item => {
const events = item.template.split(/@\{([^{}]*)\}/gi).map(key => {
if (item[key]) {
return (
......@@ -103,11 +122,10 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
render() {
const {
currentUser,
currentUserLoading,
project: { notice },
projectNotice,
projectLoading,
activitiesLoading,
chart: { radarData },
radarData,
} = this.props;
const pageHeaderContent =
......@@ -150,7 +168,6 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
return (
<PageHeaderWrapper
loading={currentUserLoading}
content={pageHeaderContent}
extraContent={extraContent}
>
......@@ -165,7 +182,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
loading={projectLoading}
bodyStyle={{ padding: 0 }}
>
{notice.map(item => (
{projectNotice.map(item => (
<Card.Grid className={styles.projectGrid} key={item.id}>
<Card bodyStyle={{ padding: 0 }} bordered={false}>
<Card.Meta
......@@ -228,7 +245,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
>
<div className={styles.members}>
<Row gutter={48}>
{notice.map(item => (
{projectNotice.map(item => (
<Col span={12} key={`members-item-${item.id}`}>
<Link to={item.href}>
<Avatar src={item.logo} size="small" />
......
import { EffectsCommandMap } from 'dva';
import { Reducer, AnyAction } from 'redux';
import { queryCurrent, queryProjectNotice, queryActivities, fakeChartData } from './service';
import { CurrentUser, Notice, Activeties, RadarData } from './data';
export default {
export interface ModalState {
currentUser: Partial<CurrentUser>;
projectNotice: Notice[];
activities: Activeties[];
radarData: RadarData[];
}
export type Effect = (
action: AnyAction,
effects: EffectsCommandMap & { select: <T>(func: (state: ModalState) => T) => T }
) => void;
export interface ModelType {
namespace: string;
state: ModalState;
reducers: {
save: Reducer<ModalState>;
clear: Reducer<ModalState>;
};
effects: {
init: Effect;
fetchUserCurrent: Effect;
fetchProjectNotice: Effect;
fetchActivitiesList: Effect;
fetchChart: Effect;
};
}
const Model: ModelType = {
namespace: 'BLOCK_NAME_CAMEL_CASE',
state: {
user: {
currentUser: {},
},
project: {
notice: [],
},
activities: {
list: [],
},
chart: {
visitData: [],
visitData2: [],
salesData: [],
searchData: [],
offlineData: [],
offlineChartData: [],
salesTypeData: [],
salesTypeDataOnline: [],
salesTypeDataOffline: [],
radarData: [],
loading: false,
},
},
reducers: {
saveCurrentUser(state, action) {
return {
...state,
user: {
currentUser: action.payload || {},
},
};
},
saveNotice(state, action) {
return {
...state,
project: {
notice: action.payload,
},
};
},
saveList(state, action) {
return {
...state,
activities: {
list: action.payload,
},
};
},
saveChart(state, { payload }) {
return {
...state,
chart: {
...payload,
},
};
},
clear(state) {
return {
...state,
chart: {
visitData: [],
visitData2: [],
salesData: [],
searchData: [],
offlineData: [],
offlineChartData: [],
salesTypeData: [],
salesTypeDataOnline: [],
salesTypeDataOffline: [],
radarData: [],
},
};
},
currentUser: {},
projectNotice: [],
activities: [],
radarData: [],
},
effects: {
*init(_, { put }) {
......@@ -87,30 +49,56 @@ export default {
*fetchUserCurrent(_, { call, put }) {
const response = yield call(queryCurrent);
yield put({
type: 'saveCurrentUser',
payload: response,
type: 'save',
payload: {
currentUser: response,
},
});
},
*fetchProjectNotice(_, { call, put }) {
const response = yield call(queryProjectNotice);
yield put({
type: 'saveNotice',
payload: Array.isArray(response) ? response : [],
type: 'save',
payload: {
projectNotice: Array.isArray(response) ? response : [],
}
});
},
*fetchActivitiesList(_, { call, put }) {
const response = yield call(queryActivities);
yield put({
type: 'saveList',
payload: Array.isArray(response) ? response : [],
type: 'save',
payload: {
activities: Array.isArray(response) ? response : [],
}
});
},
*fetchChart(_, { call, put }) {
const response = yield call(fakeChartData);
const { radarData } = yield call(fakeChartData);
yield put({
type: 'saveChart',
payload: response,
type: 'save',
payload: {
radarData,
},
});
},
},
reducers: {
save(state, { payload }) {
return {
...state,
...payload,
};
},
clear() {
return {
currentUser: {},
projectNotice: [],
activities: [],
radarData: [],
};
},
},
};
export default Model;
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