first commit
This commit is contained in:
9
src/App.test.tsx
Normal file
9
src/App.test.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
136
src/App.tsx
Normal file
136
src/App.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Layout, notification } from 'antd';
|
||||
import umbrella from 'umbrella-storage';
|
||||
import { useAlita } from 'redux-alita';
|
||||
import Routes from './routes';
|
||||
import SiderCustom from './components/SiderCustom';
|
||||
import HeaderCustom from './components/HeaderCustom';
|
||||
import { ThemePicker, Copyright } from './components/widget';
|
||||
import { checkLogin } from './utils';
|
||||
import { fetchMenu } from './service';
|
||||
import classNames from 'classnames';
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
|
||||
const { Content, Footer } = Layout;
|
||||
|
||||
type AppProps = {};
|
||||
|
||||
function checkIsMobile() {
|
||||
const clientWidth = window.innerWidth;
|
||||
return clientWidth <= 992;
|
||||
}
|
||||
|
||||
let _resizeThrottled = false;
|
||||
function resizeListener(handler: (isMobile: boolean) => void) {
|
||||
const delay = 250;
|
||||
if (!_resizeThrottled) {
|
||||
_resizeThrottled = true;
|
||||
const timer = setTimeout(() => {
|
||||
handler(checkIsMobile());
|
||||
_resizeThrottled = false;
|
||||
clearTimeout(timer);
|
||||
}, delay);
|
||||
}
|
||||
}
|
||||
function handleResize(handler: (isMobile: boolean) => void) {
|
||||
window.addEventListener('resize', resizeListener.bind(null, handler));
|
||||
}
|
||||
|
||||
function openFNotification() {
|
||||
const openNotification = () => {
|
||||
notification.open({
|
||||
message: '博主-yezihaohao',
|
||||
description: (
|
||||
<div>
|
||||
<p>
|
||||
GitHub地址:
|
||||
<a
|
||||
href="https://github.com/yezihaohao"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
https://github.com/yezihaohao
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
博客地址:
|
||||
<a
|
||||
href="https://yezihaohao.github.io/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
https://yezihaohao.github.io/
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
icon: <SmileOutlined style={{ color: 'red' }} />,
|
||||
duration: 0,
|
||||
});
|
||||
umbrella.setLocalStorage('hideBlog', true);
|
||||
};
|
||||
const storageFirst = umbrella.getLocalStorage('hideBlog');
|
||||
if (!storageFirst) {
|
||||
openNotification();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务端异步菜单
|
||||
* @param handler 执行回调
|
||||
*/
|
||||
function fetchSmenu(handler: any) {
|
||||
const setAlitaMenu = (menus: any) => {
|
||||
handler(menus);
|
||||
// this.props.setAlitaState({ stateName: 'smenus', data: menus });
|
||||
};
|
||||
setAlitaMenu(umbrella.getLocalStorage('smenus') || []);
|
||||
fetchMenu().then((smenus) => {
|
||||
setAlitaMenu(smenus);
|
||||
umbrella.setLocalStorage('smenus', smenus);
|
||||
});
|
||||
}
|
||||
|
||||
const App = (props: AppProps) => {
|
||||
const [collapsed, setCollapsed] = useState<boolean>(false);
|
||||
const [auth, responsive, setAlita] = useAlita(
|
||||
{ auth: { permissions: null } },
|
||||
{ responsive: { isMobile: false } },
|
||||
{ light: true }
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let user = umbrella.getLocalStorage('user');
|
||||
user && setAlita('auth', user);
|
||||
setAlita('responsive', { isMobile: checkIsMobile() });
|
||||
|
||||
handleResize((isMobile: boolean) => setAlita('responsive', { isMobile }));
|
||||
openFNotification();
|
||||
fetchSmenu((smenus: any[]) => setAlita('smenus', smenus));
|
||||
}, [setAlita]);
|
||||
|
||||
function toggle() {
|
||||
setCollapsed(!collapsed);
|
||||
}
|
||||
return (
|
||||
<Layout>
|
||||
{!responsive.isMobile && checkLogin(auth.permissions) && (
|
||||
<SiderCustom collapsed={collapsed} />
|
||||
)}
|
||||
<ThemePicker />
|
||||
<Layout
|
||||
className={classNames('app_layout', { 'app_layout-mobile': responsive.isMobile })}
|
||||
>
|
||||
<HeaderCustom toggle={toggle} collapsed={collapsed} user={auth || {}} />
|
||||
<Content className="app_layout_content">
|
||||
<Routes auth={auth} />
|
||||
</Content>
|
||||
<Footer className="app_layout_foot">
|
||||
<Copyright />
|
||||
</Footer>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
17
src/Page.tsx
Normal file
17
src/Page.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
|
||||
import NotFound from './components/pages/NotFound';
|
||||
import Login from './components/pages/Login';
|
||||
import App from './App';
|
||||
|
||||
export default () => (
|
||||
<Router>
|
||||
<Switch>
|
||||
<Route exact path="/" render={() => <Redirect to="/app/dashboard/index" push />} />
|
||||
<Route path="/app" component={App} />
|
||||
<Route path="/404" component={NotFound} />
|
||||
<Route path="/login" component={Login} />
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
</Router>
|
||||
);
|
||||
137
src/components/HeaderCustom.tsx
Normal file
137
src/components/HeaderCustom.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/13.
|
||||
*/
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import screenfull from 'screenfull';
|
||||
import avater from '../style/imgs/b1.jpg';
|
||||
import SiderCustom from './SiderCustom';
|
||||
import { Menu, Layout, Badge, Popover } from 'antd';
|
||||
import { gitOauthToken, gitOauthInfo } from '../service';
|
||||
import { parseQuery } from '../utils';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { PwaInstaller } from './widget';
|
||||
import { useAlita } from 'redux-alita';
|
||||
import umbrella from 'umbrella-storage';
|
||||
import { useSwitch } from '../utils/hooks';
|
||||
import {
|
||||
ArrowsAltOutlined,
|
||||
BarsOutlined,
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
NotificationOutlined,
|
||||
} from '@ant-design/icons';
|
||||
const { Header } = Layout;
|
||||
const SubMenu = Menu.SubMenu;
|
||||
const MenuItemGroup = Menu.ItemGroup;
|
||||
|
||||
type HeaderCustomProps = {
|
||||
toggle: () => void;
|
||||
collapsed: boolean;
|
||||
user: any;
|
||||
responsive?: any;
|
||||
path?: string;
|
||||
};
|
||||
|
||||
const HeaderCustom = (props: HeaderCustomProps) => {
|
||||
const [user, setUser] = useState<any>();
|
||||
const [responsive] = useAlita('responsive', { light: true });
|
||||
const [visible, turn] = useSwitch();
|
||||
const history = useHistory();
|
||||
|
||||
useEffect(() => {
|
||||
const query = parseQuery();
|
||||
let storageUser = umbrella.getLocalStorage('user');
|
||||
|
||||
if (!storageUser && query.code) {
|
||||
gitOauthToken(query.code as string).then((res: any) => {
|
||||
gitOauthInfo(res.access_token).then((info: any) => {
|
||||
setUser({
|
||||
user: info,
|
||||
});
|
||||
umbrella.setLocalStorage('user', info);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setUser({
|
||||
user: storageUser,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const screenFull = () => {
|
||||
if (screenfull.isEnabled) {
|
||||
screenfull.toggle();
|
||||
}
|
||||
};
|
||||
const menuClick = (e: any) => {
|
||||
e.key === 'logout' && logout();
|
||||
};
|
||||
const logout = () => {
|
||||
umbrella.removeLocalStorage('user');
|
||||
history.push('/login');
|
||||
};
|
||||
return (
|
||||
<Header className="custom-theme header">
|
||||
{responsive?.isMobile ? (
|
||||
<Popover
|
||||
content={<SiderCustom popoverHide={turn.turnOff} />}
|
||||
trigger="click"
|
||||
placement="bottomLeft"
|
||||
visible={visible}
|
||||
onVisibleChange={(visible) => (visible ? turn.turnOn() : turn.turnOff())}
|
||||
>
|
||||
<BarsOutlined className="header__trigger custom-trigger" />
|
||||
</Popover>
|
||||
) : props.collapsed ? (
|
||||
<MenuUnfoldOutlined
|
||||
className="header__trigger custom-trigger"
|
||||
onClick={props.toggle}
|
||||
/>
|
||||
) : (
|
||||
<MenuFoldOutlined
|
||||
className="header__trigger custom-trigger"
|
||||
onClick={props.toggle}
|
||||
/>
|
||||
)}
|
||||
<Menu
|
||||
mode="horizontal"
|
||||
style={{ lineHeight: '64px', float: 'right' }}
|
||||
onClick={menuClick}
|
||||
>
|
||||
<Menu.Item key="pwa">
|
||||
<PwaInstaller />
|
||||
</Menu.Item>
|
||||
<Menu.Item key="full">
|
||||
<ArrowsAltOutlined onClick={screenFull} />
|
||||
</Menu.Item>
|
||||
<Menu.Item key="1">
|
||||
<Badge count={25} overflowCount={10} style={{ marginLeft: 10 }}>
|
||||
<NotificationOutlined />
|
||||
</Badge>
|
||||
</Menu.Item>
|
||||
<SubMenu
|
||||
title={
|
||||
<span className="avatar">
|
||||
<img src={avater} alt="头像" />
|
||||
<i className="on bottom b-white" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<MenuItemGroup title="用户中心">
|
||||
<Menu.Item key="setting:1">你好 - {user?.userName}</Menu.Item>
|
||||
<Menu.Item key="setting:2">个人信息</Menu.Item>
|
||||
<Menu.Item key="logout">
|
||||
<span onClick={logout}>退出登录</span>
|
||||
</Menu.Item>
|
||||
</MenuItemGroup>
|
||||
<MenuItemGroup title="设置中心">
|
||||
<Menu.Item key="setting:3">个人设置</Menu.Item>
|
||||
<Menu.Item key="setting:4">系统设置</Menu.Item>
|
||||
</MenuItemGroup>
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderCustom;
|
||||
14
src/components/Page.tsx
Normal file
14
src/components/Page.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/16.
|
||||
*/
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
interface PageProps {
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
const Page = (props: PageProps) => {
|
||||
return <div style={{ height: '100%' }}>{props.children}</div>;
|
||||
};
|
||||
|
||||
export default Page;
|
||||
103
src/components/SiderCustom.tsx
Normal file
103
src/components/SiderCustom.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/13.
|
||||
*/
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Layout } from 'antd';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import routes from '../routes/config';
|
||||
import SiderMenu from './SiderMenu';
|
||||
import { useAlita } from 'redux-alita';
|
||||
import { useSwitch } from '../utils/hooks';
|
||||
import { usePrevious } from 'ahooks';
|
||||
const { Sider } = Layout;
|
||||
|
||||
type SiderCustomProps = RouteComponentProps<any> & {
|
||||
popoverHide?: () => void;
|
||||
collapsed?: boolean;
|
||||
smenus?: any;
|
||||
};
|
||||
interface IMenu {
|
||||
openKeys: string[];
|
||||
selectedKey: string;
|
||||
}
|
||||
|
||||
const SiderCustom = (props: SiderCustomProps) => {
|
||||
const [collapsed, tCollapsed] = useSwitch();
|
||||
const [firstHide, tFirstHide] = useSwitch();
|
||||
const [menu, setMenu] = useState<IMenu>({ openKeys: [''], selectedKey: '' });
|
||||
// 异步菜单
|
||||
const [smenus] = useAlita({ smenus: [] }, { light: true });
|
||||
const { location, collapsed: pCollapsed } = props;
|
||||
const prePathname = usePrevious(props.location.pathname);
|
||||
|
||||
useEffect(() => {
|
||||
const recombineOpenKeys = (openKeys: string[]) => {
|
||||
let i = 0;
|
||||
let strPlus = '';
|
||||
let tempKeys: string[] = [];
|
||||
// 多级菜单循环处理
|
||||
while (i < openKeys.length) {
|
||||
strPlus += openKeys[i];
|
||||
tempKeys.push(strPlus);
|
||||
i++;
|
||||
}
|
||||
return tempKeys;
|
||||
};
|
||||
const getOpenAndSelectKeys = () => {
|
||||
return {
|
||||
openKeys: recombineOpenKeys(location.pathname.match(/[/](\w+)/gi) || []),
|
||||
selectedKey: location.pathname,
|
||||
};
|
||||
};
|
||||
|
||||
if (pCollapsed !== collapsed) {
|
||||
setMenu(getOpenAndSelectKeys());
|
||||
tCollapsed.setSwitcher(!!pCollapsed);
|
||||
tFirstHide.setSwitcher(!!pCollapsed);
|
||||
}
|
||||
|
||||
if (prePathname !== location.pathname) {
|
||||
setMenu(getOpenAndSelectKeys());
|
||||
}
|
||||
}, [prePathname, location.pathname, collapsed, tFirstHide, tCollapsed, pCollapsed]);
|
||||
|
||||
const menuClick = (e: any) => {
|
||||
setMenu((state) => ({ ...state, selectedKey: e.key }));
|
||||
props.popoverHide?.(); // 响应式布局控制小屏幕点击菜单时隐藏菜单操作
|
||||
};
|
||||
|
||||
const openMenu: any = (v: string[]) => {
|
||||
setMenu((state) => ({ ...state, openKeys: v }));
|
||||
tFirstHide.turnOff();
|
||||
};
|
||||
|
||||
return (
|
||||
<Sider
|
||||
trigger={null}
|
||||
breakpoint="lg"
|
||||
collapsed={collapsed}
|
||||
style={{ overflowY: 'auto' }}
|
||||
className="sider-custom"
|
||||
>
|
||||
<div className="logo" />
|
||||
<SiderMenu
|
||||
menus={[...routes.menus, ...smenus]}
|
||||
onClick={menuClick}
|
||||
mode="inline"
|
||||
selectedKeys={[menu.selectedKey]}
|
||||
openKeys={firstHide ? [] : menu.openKeys}
|
||||
onOpenChange={openMenu}
|
||||
/>
|
||||
<style>
|
||||
{`
|
||||
#nprogress .spinner{
|
||||
left: ${collapsed ? '70px' : '206px'};
|
||||
right: 0 !important;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</Sider>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(SiderCustom);
|
||||
99
src/components/SiderMenu.tsx
Normal file
99
src/components/SiderMenu.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Menu } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
||||
import { IFMenu } from '../routes/config';
|
||||
import { MenuProps } from 'antd/lib/menu';
|
||||
|
||||
const renderMenuItem = (
|
||||
item: IFMenu // item.route 菜单单独跳转的路由
|
||||
) => (
|
||||
<Menu.Item key={item.key}>
|
||||
<Link to={(item.route || item.key) + (item.query || '')}>
|
||||
{/* {item.icon && <Icon type={item.icon} />} */}
|
||||
<span className="nav-text">{item.title}</span>
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
const renderSubMenu = (item: IFMenu) => {
|
||||
return (
|
||||
<Menu.SubMenu
|
||||
key={item.key}
|
||||
title={
|
||||
<span>
|
||||
{/* {item.icon && <Icon type={item.icon} />} */}
|
||||
<span className="nav-text">{item.title}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{item.subs!.map((sub) => (sub.subs ? renderSubMenu(sub) : renderMenuItem(sub)))}
|
||||
</Menu.SubMenu>
|
||||
);
|
||||
};
|
||||
|
||||
type SiderMenuProps = MenuProps & {
|
||||
menus: any;
|
||||
onClick: (e: any) => void;
|
||||
selectedKeys: string[];
|
||||
openKeys?: string[];
|
||||
onOpenChange: (v: string[]) => void;
|
||||
};
|
||||
|
||||
const SiderMenu = ({ menus, ...props }: SiderMenuProps) => {
|
||||
const [dragItems, setDragItems] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setDragItems(menus);
|
||||
}, [menus]);
|
||||
|
||||
const reorder = (list: any, startIndex: number, endIndex: number) => {
|
||||
const result = Array.from(list);
|
||||
const [removed] = result.splice(startIndex, 1);
|
||||
result.splice(endIndex, 0, removed);
|
||||
return result;
|
||||
};
|
||||
const onDragEnd = (result: any) => {
|
||||
// dropped outside the list
|
||||
if (!result.destination) {
|
||||
return;
|
||||
}
|
||||
|
||||
const _items = reorder(dragItems, result.source.index, result.destination.index);
|
||||
setDragItems(_items);
|
||||
};
|
||||
return (
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<Droppable droppableId="droppable">
|
||||
{(provided, snapshot) => (
|
||||
<div ref={provided.innerRef} {...provided.droppableProps}>
|
||||
{dragItems.map((item: IFMenu, index: number) => (
|
||||
<Draggable key={item.key} draggableId={item.key} index={index}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
onDragStart={(e: React.DragEvent<any>) =>
|
||||
provided.dragHandleProps &&
|
||||
provided.dragHandleProps.onDragStart(e as any)
|
||||
}
|
||||
>
|
||||
<Menu {...props}>
|
||||
{item.subs!
|
||||
? renderSubMenu(item)
|
||||
: renderMenuItem(item)}
|
||||
</Menu>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SiderMenu);
|
||||
137
src/components/animation/BasicAnimations.tsx
Normal file
137
src/components/animation/BasicAnimations.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/5/8.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card, Switch } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
|
||||
class BasicAnimations extends React.Component {
|
||||
state = {
|
||||
animated: false,
|
||||
animatedOne: -1,
|
||||
};
|
||||
animatedAll = (checked: boolean) => {
|
||||
checked && this.setState({ animated: true });
|
||||
!checked && this.setState({ animated: false });
|
||||
};
|
||||
animatedOne = (i: number) => {
|
||||
this.setState({ animatedOne: i });
|
||||
};
|
||||
animatedOneOver = () => {
|
||||
this.setState({ animatedOne: -1 });
|
||||
};
|
||||
render() {
|
||||
const animations = [
|
||||
'bounce',
|
||||
'flash',
|
||||
'rubberBand',
|
||||
'shake',
|
||||
'headShake',
|
||||
'swing',
|
||||
'tada',
|
||||
'wobble',
|
||||
'jello',
|
||||
'bounceIn',
|
||||
'bounceInDown',
|
||||
'bounceInLeft',
|
||||
'bounceInRight',
|
||||
'bounceOut',
|
||||
'bounceOutDown',
|
||||
'bounceOutLeft',
|
||||
'bounceOutLeft',
|
||||
'bounceOutUp',
|
||||
'fadeIn',
|
||||
'fadeInDown',
|
||||
'fadeInDownBig',
|
||||
'fadeInLeft',
|
||||
'fadeInLeftBig',
|
||||
'fadeInRight',
|
||||
'fadeInRightBig',
|
||||
'fadeInUp',
|
||||
'fadeInUpBig',
|
||||
'fadeOut',
|
||||
'fadeOutDown',
|
||||
'fadeOutDownBig',
|
||||
'fadeOutLeft',
|
||||
'fadeOutLeftBig',
|
||||
'fadeOutRight',
|
||||
'fadeOutRightBig',
|
||||
'fadeOutUp',
|
||||
'fadeOutUpBig',
|
||||
'flipInX',
|
||||
'flipInY',
|
||||
'flipOutX',
|
||||
'flipOutY',
|
||||
'lightSpeedIn',
|
||||
'lightSpeedOut',
|
||||
'rotateIn',
|
||||
'rotateInDownLeft',
|
||||
'rotateInDownRight',
|
||||
'rotateInUpLeft',
|
||||
'rotateInUpRight',
|
||||
'rotateOut',
|
||||
'rotateOutDownLeft',
|
||||
'rotateOutDownRight',
|
||||
'rotateOutUpLeft',
|
||||
'rotateOutUpRight',
|
||||
'hinge',
|
||||
'jackInTheBox',
|
||||
'rollIn',
|
||||
'rollOut',
|
||||
'zoomIn',
|
||||
'zoomInDown',
|
||||
'zoomInLeft',
|
||||
'zoomInRight',
|
||||
'zoomInUp',
|
||||
'zoomOut',
|
||||
'zoomOutDown',
|
||||
'zoomOutLeft',
|
||||
'zoomOutRight',
|
||||
'zoomOutUp',
|
||||
'slideInDown',
|
||||
'slideInLeft',
|
||||
'slideInRight',
|
||||
'slideInUp',
|
||||
'slideOutDown',
|
||||
'slideOutLeft',
|
||||
'slideOutRight',
|
||||
'slideOutUp',
|
||||
];
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom breads={['动画', '基础动画']} />
|
||||
<Row className="mb-m">
|
||||
<span className="mr-s">全部动画(单个动画请移动鼠标)</span>
|
||||
<Switch onChange={this.animatedAll} />
|
||||
</Row>
|
||||
<Row gutter={14}>
|
||||
{animations.map((v, i) => (
|
||||
<Col className="gutter-row" md={6} key={i}>
|
||||
<div className="gutter-box">
|
||||
<Card
|
||||
className={`${
|
||||
this.state.animated || this.state.animatedOne === i
|
||||
? 'animated'
|
||||
: ''
|
||||
} ${
|
||||
this.state.animated || this.state.animatedOne === i
|
||||
? 'infinite'
|
||||
: ''
|
||||
} ${v}`}
|
||||
onMouseEnter={() => this.animatedOne(i)}
|
||||
onMouseLeave={() => this.animatedOneOver()}
|
||||
>
|
||||
<div className="pa-m text-center">
|
||||
<h3>{v}</h3>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default BasicAnimations;
|
||||
120
src/components/animation/ExampleAnimations.tsx
Normal file
120
src/components/animation/ExampleAnimations.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/5/8.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card, Table, Popconfirm, Button } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
|
||||
type ExampleAnimationsProps = {};
|
||||
type ExampleAnimationsState = {
|
||||
dataSource: any;
|
||||
count: number;
|
||||
deleteIndex: number;
|
||||
};
|
||||
|
||||
class ExampleAnimations extends React.Component<ExampleAnimationsProps, ExampleAnimationsState> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.columns = [
|
||||
{
|
||||
title: 'name',
|
||||
dataIndex: 'name',
|
||||
width: '30%',
|
||||
},
|
||||
{
|
||||
title: 'age',
|
||||
dataIndex: 'age',
|
||||
},
|
||||
{
|
||||
title: 'address',
|
||||
dataIndex: 'address',
|
||||
},
|
||||
{
|
||||
title: 'operation',
|
||||
dataIndex: 'operation',
|
||||
render: (text: any, record: any, index: number) => {
|
||||
return this.state.dataSource.length > 1 ? (
|
||||
<Popconfirm
|
||||
title="Sure to delete?"
|
||||
onConfirm={() => this.onDelete(record, index)}
|
||||
>
|
||||
<span>Delete</span>
|
||||
</Popconfirm>
|
||||
) : null;
|
||||
},
|
||||
},
|
||||
];
|
||||
this.state = {
|
||||
dataSource: [
|
||||
{
|
||||
key: '0',
|
||||
name: 'Edward King 0',
|
||||
age: '32',
|
||||
address: 'London, Park Lane no. 0',
|
||||
},
|
||||
{
|
||||
key: '1',
|
||||
name: 'Edward King 1',
|
||||
age: '32',
|
||||
address: 'London, Park Lane no. 1',
|
||||
},
|
||||
],
|
||||
count: 2,
|
||||
deleteIndex: -1,
|
||||
};
|
||||
}
|
||||
columns: any;
|
||||
onDelete = (record: any, index: number) => {
|
||||
const dataSource = [...this.state.dataSource];
|
||||
dataSource.splice(index, 1);
|
||||
this.setState({ deleteIndex: record.key });
|
||||
setTimeout(() => {
|
||||
this.setState({ dataSource });
|
||||
}, 500);
|
||||
};
|
||||
handleAdd = () => {
|
||||
const { count, dataSource } = this.state;
|
||||
const newData = {
|
||||
key: count,
|
||||
name: `Edward King ${count}`,
|
||||
age: 32,
|
||||
address: `London, Park Lane no. ${count}`,
|
||||
};
|
||||
this.setState({
|
||||
dataSource: [newData, ...dataSource],
|
||||
count: count + 1,
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const { dataSource } = this.state;
|
||||
const columns = this.columns;
|
||||
return (
|
||||
<div className="gutter-example">
|
||||
<BreadcrumbCustom breads={['动画', '动画案例']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Button className="editable-add-btn mb-s" onClick={this.handleAdd}>
|
||||
Add
|
||||
</Button>
|
||||
<Table
|
||||
bordered
|
||||
dataSource={dataSource}
|
||||
columns={columns}
|
||||
rowClassName={(record: any, index: number) => {
|
||||
if (this.state.deleteIndex === record.key)
|
||||
return 'animated zoomOutLeft min-black';
|
||||
return 'animated fadeInRight';
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ExampleAnimations;
|
||||
62
src/components/auth/Basic.tsx
Normal file
62
src/components/auth/Basic.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Created by 叶子 on 2017/7/31.
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import beauty from '@/style/imgs/beauty.jpg';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { AuthWidget } from '../widget';
|
||||
|
||||
class Basic extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<BreadcrumbCustom breads={['权限管理', '基础演示']} />
|
||||
<AuthWidget
|
||||
children={(auth: any) => (
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Card bordered={false} bodyStyle={{ minHeight: 600 }}>
|
||||
{!auth.uid && (
|
||||
<h2 style={{ height: 500 }} className="center">
|
||||
登录之后你将看到一张美女图
|
||||
</h2>
|
||||
)}
|
||||
{auth.permissions &&
|
||||
auth.permissions.includes('auth/authPage/visit') && (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<img src={beauty} alt="" style={{ height: 400 }} />
|
||||
{(auth.permissions.includes(
|
||||
'auth/authPage/edit'
|
||||
) && (
|
||||
<div>
|
||||
<p>
|
||||
看啥子美女,看点美景就行啦~
|
||||
<span
|
||||
role="img"
|
||||
aria-label=""
|
||||
aria-labelledby=""
|
||||
>
|
||||
😄😄
|
||||
</span>
|
||||
</p>
|
||||
<p>管理员身份登录才能看到这两段话</p>
|
||||
</div>
|
||||
)) || (
|
||||
<div>
|
||||
<p>管理员登录将看到不一样的效果</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Basic;
|
||||
38
src/components/auth/RouterEnter.tsx
Normal file
38
src/components/auth/RouterEnter.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Created by 叶子 on 2017/8/1.
|
||||
*/
|
||||
/**
|
||||
* Created by 叶子 on 2017/7/31.
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { AuthWidget } from '../widget';
|
||||
|
||||
class RouterEnter extends Component {
|
||||
componentDidMount() {
|
||||
console.log('RouterEnter');
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<BreadcrumbCustom breads={['权限管理', '路由拦截']} />
|
||||
<AuthWidget
|
||||
children={(auth: any) => (
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Card bordered={false} bodyStyle={{ minHeight: 600 }}>
|
||||
<h2 style={{ height: 500 }} className="center">
|
||||
只有管理员登录才能看到该页面,否则跳转到404页面
|
||||
</h2>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RouterEnter;
|
||||
55
src/components/charts/Echarts.tsx
Normal file
55
src/components/charts/Echarts.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/17.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import EchartsArea from './EchartsArea';
|
||||
import EchartsPie from './EchartsPie';
|
||||
import EchartsEffectScatter from './EchartsEffectScatter';
|
||||
import EchartsForce from './EchartsForce';
|
||||
|
||||
class Echarts extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="gutter-example">
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="区域图" bordered={false}>
|
||||
<EchartsArea />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card title="关系图" bordered={false}>
|
||||
{/*<EchartsGraphnpm />*/}
|
||||
<EchartsForce />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card title="饼图" bordered={false}>
|
||||
<EchartsPie />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="散点图" bordered={false}>
|
||||
<EchartsEffectScatter />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Echarts;
|
||||
113
src/components/charts/EchartsArea.tsx
Normal file
113
src/components/charts/EchartsArea.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/17.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import echarts from 'echarts';
|
||||
|
||||
let base = +new Date(1968, 9, 3);
|
||||
let oneDay = 24 * 3600 * 1000;
|
||||
let date = [];
|
||||
|
||||
let data = [Math.random() * 300];
|
||||
|
||||
for (var i = 1; i < 20000; i++) {
|
||||
var now = new Date((base += oneDay));
|
||||
date.push([now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'));
|
||||
data.push(Math.round((Math.random() - 0.5) * 20 + data[i - 1]));
|
||||
}
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
position: function(pt: any) {
|
||||
return [pt[0], '10%'];
|
||||
},
|
||||
},
|
||||
title: {
|
||||
left: 'center',
|
||||
text: '大数据量面积图',
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none',
|
||||
},
|
||||
restore: {},
|
||||
saveAsImage: {},
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: date,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%'],
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 10,
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 10,
|
||||
handleIcon:
|
||||
'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
|
||||
handleSize: '80%',
|
||||
handleStyle: {
|
||||
color: '#fff',
|
||||
shadowBlur: 3,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.6)',
|
||||
shadowOffsetX: 2,
|
||||
shadowOffsetY: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '模拟数据',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgb(255, 70, 131)',
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgb(255, 158, 68)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgb(255, 70, 131)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
},
|
||||
data: data,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
class EchartsArea extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ReactEcharts
|
||||
option={option}
|
||||
style={{ height: '300px', width: '100%' }}
|
||||
className={'react_for_echarts'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EchartsArea;
|
||||
522
src/components/charts/EchartsEffectScatter.tsx
Normal file
522
src/components/charts/EchartsEffectScatter.tsx
Normal file
@@ -0,0 +1,522 @@
|
||||
/**
|
||||
* Created by SEELE on 2017/8/23.
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
require('echarts/map/js/china.js');
|
||||
|
||||
const data = [
|
||||
{ name: '海门', value: 9 },
|
||||
{ name: '鄂尔多斯', value: 12 },
|
||||
{ name: '招远', value: 12 },
|
||||
{ name: '舟山', value: 12 },
|
||||
{ name: '齐齐哈尔', value: 14 },
|
||||
{ name: '盐城', value: 15 },
|
||||
{ name: '赤峰', value: 16 },
|
||||
{ name: '青岛', value: 18 },
|
||||
{ name: '乳山', value: 18 },
|
||||
{ name: '金昌', value: 19 },
|
||||
{ name: '泉州', value: 21 },
|
||||
{ name: '莱西', value: 21 },
|
||||
{ name: '日照', value: 21 },
|
||||
{ name: '胶南', value: 22 },
|
||||
{ name: '南通', value: 23 },
|
||||
{ name: '拉萨', value: 24 },
|
||||
{ name: '云浮', value: 24 },
|
||||
{ name: '梅州', value: 25 },
|
||||
{ name: '文登', value: 25 },
|
||||
{ name: '上海', value: 25 },
|
||||
{ name: '攀枝花', value: 25 },
|
||||
{ name: '威海', value: 25 },
|
||||
{ name: '承德', value: 25 },
|
||||
{ name: '厦门', value: 26 },
|
||||
{ name: '汕尾', value: 26 },
|
||||
{ name: '潮州', value: 26 },
|
||||
{ name: '丹东', value: 27 },
|
||||
{ name: '太仓', value: 27 },
|
||||
{ name: '曲靖', value: 27 },
|
||||
{ name: '烟台', value: 28 },
|
||||
{ name: '福州', value: 29 },
|
||||
{ name: '瓦房店', value: 30 },
|
||||
{ name: '即墨', value: 30 },
|
||||
{ name: '抚顺', value: 31 },
|
||||
{ name: '玉溪', value: 31 },
|
||||
{ name: '张家口', value: 31 },
|
||||
{ name: '阳泉', value: 31 },
|
||||
{ name: '莱州', value: 32 },
|
||||
{ name: '湖州', value: 32 },
|
||||
{ name: '汕头', value: 32 },
|
||||
{ name: '昆山', value: 33 },
|
||||
{ name: '宁波', value: 33 },
|
||||
{ name: '湛江', value: 33 },
|
||||
{ name: '揭阳', value: 34 },
|
||||
{ name: '荣成', value: 34 },
|
||||
{ name: '连云港', value: 35 },
|
||||
{ name: '葫芦岛', value: 35 },
|
||||
{ name: '常熟', value: 36 },
|
||||
{ name: '东莞', value: 36 },
|
||||
{ name: '河源', value: 36 },
|
||||
{ name: '淮安', value: 36 },
|
||||
{ name: '泰州', value: 36 },
|
||||
{ name: '南宁', value: 37 },
|
||||
{ name: '营口', value: 37 },
|
||||
{ name: '惠州', value: 37 },
|
||||
{ name: '江阴', value: 37 },
|
||||
{ name: '蓬莱', value: 37 },
|
||||
{ name: '韶关', value: 38 },
|
||||
{ name: '嘉峪关', value: 38 },
|
||||
{ name: '广州', value: 38 },
|
||||
{ name: '延安', value: 38 },
|
||||
{ name: '太原', value: 39 },
|
||||
{ name: '清远', value: 39 },
|
||||
{ name: '中山', value: 39 },
|
||||
{ name: '昆明', value: 39 },
|
||||
{ name: '寿光', value: 40 },
|
||||
{ name: '盘锦', value: 40 },
|
||||
{ name: '长治', value: 41 },
|
||||
{ name: '深圳', value: 41 },
|
||||
{ name: '珠海', value: 42 },
|
||||
{ name: '宿迁', value: 43 },
|
||||
{ name: '咸阳', value: 43 },
|
||||
{ name: '铜川', value: 44 },
|
||||
{ name: '平度', value: 44 },
|
||||
{ name: '佛山', value: 44 },
|
||||
{ name: '海口', value: 44 },
|
||||
{ name: '江门', value: 45 },
|
||||
{ name: '章丘', value: 45 },
|
||||
{ name: '肇庆', value: 46 },
|
||||
{ name: '大连', value: 47 },
|
||||
{ name: '临汾', value: 47 },
|
||||
{ name: '吴江', value: 47 },
|
||||
{ name: '石嘴山', value: 49 },
|
||||
{ name: '沈阳', value: 50 },
|
||||
{ name: '苏州', value: 50 },
|
||||
{ name: '茂名', value: 50 },
|
||||
{ name: '嘉兴', value: 51 },
|
||||
{ name: '长春', value: 51 },
|
||||
{ name: '胶州', value: 52 },
|
||||
{ name: '银川', value: 52 },
|
||||
{ name: '张家港', value: 52 },
|
||||
{ name: '三门峡', value: 53 },
|
||||
{ name: '锦州', value: 54 },
|
||||
{ name: '南昌', value: 54 },
|
||||
{ name: '柳州', value: 54 },
|
||||
{ name: '三亚', value: 54 },
|
||||
{ name: '自贡', value: 56 },
|
||||
{ name: '吉林', value: 56 },
|
||||
{ name: '阳江', value: 57 },
|
||||
{ name: '泸州', value: 57 },
|
||||
{ name: '西宁', value: 57 },
|
||||
{ name: '宜宾', value: 58 },
|
||||
{ name: '呼和浩特', value: 58 },
|
||||
{ name: '成都', value: 58 },
|
||||
{ name: '大同', value: 58 },
|
||||
{ name: '镇江', value: 59 },
|
||||
{ name: '桂林', value: 59 },
|
||||
{ name: '张家界', value: 59 },
|
||||
{ name: '宜兴', value: 59 },
|
||||
{ name: '北海', value: 60 },
|
||||
{ name: '西安', value: 61 },
|
||||
{ name: '金坛', value: 62 },
|
||||
{ name: '东营', value: 62 },
|
||||
{ name: '牡丹江', value: 63 },
|
||||
{ name: '遵义', value: 63 },
|
||||
{ name: '绍兴', value: 63 },
|
||||
{ name: '扬州', value: 64 },
|
||||
{ name: '常州', value: 64 },
|
||||
{ name: '潍坊', value: 65 },
|
||||
{ name: '重庆', value: 66 },
|
||||
{ name: '台州', value: 67 },
|
||||
{ name: '南京', value: 67 },
|
||||
{ name: '滨州', value: 70 },
|
||||
{ name: '贵阳', value: 71 },
|
||||
{ name: '无锡', value: 71 },
|
||||
{ name: '本溪', value: 71 },
|
||||
{ name: '克拉玛依', value: 72 },
|
||||
{ name: '渭南', value: 72 },
|
||||
{ name: '马鞍山', value: 72 },
|
||||
{ name: '宝鸡', value: 72 },
|
||||
{ name: '焦作', value: 75 },
|
||||
{ name: '句容', value: 75 },
|
||||
{ name: '北京', value: 79 },
|
||||
{ name: '徐州', value: 79 },
|
||||
{ name: '衡水', value: 80 },
|
||||
{ name: '包头', value: 80 },
|
||||
{ name: '绵阳', value: 80 },
|
||||
{ name: '乌鲁木齐', value: 84 },
|
||||
{ name: '枣庄', value: 84 },
|
||||
{ name: '杭州', value: 84 },
|
||||
{ name: '淄博', value: 85 },
|
||||
{ name: '鞍山', value: 86 },
|
||||
{ name: '溧阳', value: 86 },
|
||||
{ name: '库尔勒', value: 86 },
|
||||
{ name: '安阳', value: 90 },
|
||||
{ name: '开封', value: 90 },
|
||||
{ name: '济南', value: 92 },
|
||||
{ name: '德阳', value: 93 },
|
||||
{ name: '温州', value: 95 },
|
||||
{ name: '九江', value: 96 },
|
||||
{ name: '邯郸', value: 98 },
|
||||
{ name: '临安', value: 99 },
|
||||
{ name: '兰州', value: 99 },
|
||||
{ name: '沧州', value: 100 },
|
||||
{ name: '临沂', value: 103 },
|
||||
{ name: '南充', value: 104 },
|
||||
{ name: '天津', value: 105 },
|
||||
{ name: '富阳', value: 106 },
|
||||
{ name: '泰安', value: 112 },
|
||||
{ name: '诸暨', value: 112 },
|
||||
{ name: '郑州', value: 113 },
|
||||
{ name: '哈尔滨', value: 114 },
|
||||
{ name: '聊城', value: 116 },
|
||||
{ name: '芜湖', value: 117 },
|
||||
{ name: '唐山', value: 119 },
|
||||
{ name: '平顶山', value: 119 },
|
||||
{ name: '邢台', value: 119 },
|
||||
{ name: '德州', value: 120 },
|
||||
{ name: '济宁', value: 120 },
|
||||
{ name: '荆州', value: 127 },
|
||||
{ name: '宜昌', value: 130 },
|
||||
{ name: '义乌', value: 132 },
|
||||
{ name: '丽水', value: 133 },
|
||||
{ name: '洛阳', value: 134 },
|
||||
{ name: '秦皇岛', value: 136 },
|
||||
{ name: '株洲', value: 143 },
|
||||
{ name: '石家庄', value: 147 },
|
||||
{ name: '莱芜', value: 148 },
|
||||
{ name: '常德', value: 152 },
|
||||
{ name: '保定', value: 153 },
|
||||
{ name: '湘潭', value: 154 },
|
||||
{ name: '金华', value: 157 },
|
||||
{ name: '岳阳', value: 169 },
|
||||
{ name: '长沙', value: 175 },
|
||||
{ name: '衢州', value: 177 },
|
||||
{ name: '廊坊', value: 193 },
|
||||
{ name: '菏泽', value: 194 },
|
||||
{ name: '合肥', value: 229 },
|
||||
{ name: '武汉', value: 273 },
|
||||
{ name: '大庆', value: 279 },
|
||||
];
|
||||
const geoCoordMap = {
|
||||
海门: [121.15, 31.89],
|
||||
鄂尔多斯: [109.781327, 39.608266],
|
||||
招远: [120.38, 37.35],
|
||||
舟山: [122.207216, 29.985295],
|
||||
齐齐哈尔: [123.97, 47.33],
|
||||
盐城: [120.13, 33.38],
|
||||
赤峰: [118.87, 42.28],
|
||||
青岛: [120.33, 36.07],
|
||||
乳山: [121.52, 36.89],
|
||||
金昌: [102.188043, 38.520089],
|
||||
泉州: [118.58, 24.93],
|
||||
莱西: [120.53, 36.86],
|
||||
日照: [119.46, 35.42],
|
||||
胶南: [119.97, 35.88],
|
||||
南通: [121.05, 32.08],
|
||||
拉萨: [91.11, 29.97],
|
||||
云浮: [112.02, 22.93],
|
||||
梅州: [116.1, 24.55],
|
||||
文登: [122.05, 37.2],
|
||||
上海: [121.48, 31.22],
|
||||
攀枝花: [101.718637, 26.582347],
|
||||
威海: [122.1, 37.5],
|
||||
承德: [117.93, 40.97],
|
||||
厦门: [118.1, 24.46],
|
||||
汕尾: [115.375279, 22.786211],
|
||||
潮州: [116.63, 23.68],
|
||||
丹东: [124.37, 40.13],
|
||||
太仓: [121.1, 31.45],
|
||||
曲靖: [103.79, 25.51],
|
||||
烟台: [121.39, 37.52],
|
||||
福州: [119.3, 26.08],
|
||||
瓦房店: [121.979603, 39.627114],
|
||||
即墨: [120.45, 36.38],
|
||||
抚顺: [123.97, 41.97],
|
||||
玉溪: [102.52, 24.35],
|
||||
张家口: [114.87, 40.82],
|
||||
阳泉: [113.57, 37.85],
|
||||
莱州: [119.942327, 37.177017],
|
||||
湖州: [120.1, 30.86],
|
||||
汕头: [116.69, 23.39],
|
||||
昆山: [120.95, 31.39],
|
||||
宁波: [121.56, 29.86],
|
||||
湛江: [110.359377, 21.270708],
|
||||
揭阳: [116.35, 23.55],
|
||||
荣成: [122.41, 37.16],
|
||||
连云港: [119.16, 34.59],
|
||||
葫芦岛: [120.836932, 40.711052],
|
||||
常熟: [120.74, 31.64],
|
||||
东莞: [113.75, 23.04],
|
||||
河源: [114.68, 23.73],
|
||||
淮安: [119.15, 33.5],
|
||||
泰州: [119.9, 32.49],
|
||||
南宁: [108.33, 22.84],
|
||||
营口: [122.18, 40.65],
|
||||
惠州: [114.4, 23.09],
|
||||
江阴: [120.26, 31.91],
|
||||
蓬莱: [120.75, 37.8],
|
||||
韶关: [113.62, 24.84],
|
||||
嘉峪关: [98.289152, 39.77313],
|
||||
广州: [113.23, 23.16],
|
||||
延安: [109.47, 36.6],
|
||||
太原: [112.53, 37.87],
|
||||
清远: [113.01, 23.7],
|
||||
中山: [113.38, 22.52],
|
||||
昆明: [102.73, 25.04],
|
||||
寿光: [118.73, 36.86],
|
||||
盘锦: [122.070714, 41.119997],
|
||||
长治: [113.08, 36.18],
|
||||
深圳: [114.07, 22.62],
|
||||
珠海: [113.52, 22.3],
|
||||
宿迁: [118.3, 33.96],
|
||||
咸阳: [108.72, 34.36],
|
||||
铜川: [109.11, 35.09],
|
||||
平度: [119.97, 36.77],
|
||||
佛山: [113.11, 23.05],
|
||||
海口: [110.35, 20.02],
|
||||
江门: [113.06, 22.61],
|
||||
章丘: [117.53, 36.72],
|
||||
肇庆: [112.44, 23.05],
|
||||
大连: [121.62, 38.92],
|
||||
临汾: [111.5, 36.08],
|
||||
吴江: [120.63, 31.16],
|
||||
石嘴山: [106.39, 39.04],
|
||||
沈阳: [123.38, 41.8],
|
||||
苏州: [120.62, 31.32],
|
||||
茂名: [110.88, 21.68],
|
||||
嘉兴: [120.76, 30.77],
|
||||
长春: [125.35, 43.88],
|
||||
胶州: [120.03336, 36.264622],
|
||||
银川: [106.27, 38.47],
|
||||
张家港: [120.555821, 31.875428],
|
||||
三门峡: [111.19, 34.76],
|
||||
锦州: [121.15, 41.13],
|
||||
南昌: [115.89, 28.68],
|
||||
柳州: [109.4, 24.33],
|
||||
三亚: [109.511909, 18.252847],
|
||||
自贡: [104.778442, 29.33903],
|
||||
吉林: [126.57, 43.87],
|
||||
阳江: [111.95, 21.85],
|
||||
泸州: [105.39, 28.91],
|
||||
西宁: [101.74, 36.56],
|
||||
宜宾: [104.56, 29.77],
|
||||
呼和浩特: [111.65, 40.82],
|
||||
成都: [104.06, 30.67],
|
||||
大同: [113.3, 40.12],
|
||||
镇江: [119.44, 32.2],
|
||||
桂林: [110.28, 25.29],
|
||||
张家界: [110.479191, 29.117096],
|
||||
宜兴: [119.82, 31.36],
|
||||
北海: [109.12, 21.49],
|
||||
西安: [108.95, 34.27],
|
||||
金坛: [119.56, 31.74],
|
||||
东营: [118.49, 37.46],
|
||||
牡丹江: [129.58, 44.6],
|
||||
遵义: [106.9, 27.7],
|
||||
绍兴: [120.58, 30.01],
|
||||
扬州: [119.42, 32.39],
|
||||
常州: [119.95, 31.79],
|
||||
潍坊: [119.1, 36.62],
|
||||
重庆: [106.54, 29.59],
|
||||
台州: [121.420757, 28.656386],
|
||||
南京: [118.78, 32.04],
|
||||
滨州: [118.03, 37.36],
|
||||
贵阳: [106.71, 26.57],
|
||||
无锡: [120.29, 31.59],
|
||||
本溪: [123.73, 41.3],
|
||||
克拉玛依: [84.77, 45.59],
|
||||
渭南: [109.5, 34.52],
|
||||
马鞍山: [118.48, 31.56],
|
||||
宝鸡: [107.15, 34.38],
|
||||
焦作: [113.21, 35.24],
|
||||
句容: [119.16, 31.95],
|
||||
北京: [116.46, 39.92],
|
||||
徐州: [117.2, 34.26],
|
||||
衡水: [115.72, 37.72],
|
||||
包头: [110, 40.58],
|
||||
绵阳: [104.73, 31.48],
|
||||
乌鲁木齐: [87.68, 43.77],
|
||||
枣庄: [117.57, 34.86],
|
||||
杭州: [120.19, 30.26],
|
||||
淄博: [118.05, 36.78],
|
||||
鞍山: [122.85, 41.12],
|
||||
溧阳: [119.48, 31.43],
|
||||
库尔勒: [86.06, 41.68],
|
||||
安阳: [114.35, 36.1],
|
||||
开封: [114.35, 34.79],
|
||||
济南: [117, 36.65],
|
||||
德阳: [104.37, 31.13],
|
||||
温州: [120.65, 28.01],
|
||||
九江: [115.97, 29.71],
|
||||
邯郸: [114.47, 36.6],
|
||||
临安: [119.72, 30.23],
|
||||
兰州: [103.73, 36.03],
|
||||
沧州: [116.83, 38.33],
|
||||
临沂: [118.35, 35.05],
|
||||
南充: [106.110698, 30.837793],
|
||||
天津: [117.2, 39.13],
|
||||
富阳: [119.95, 30.07],
|
||||
泰安: [117.13, 36.18],
|
||||
诸暨: [120.23, 29.71],
|
||||
郑州: [113.65, 34.76],
|
||||
哈尔滨: [126.63, 45.75],
|
||||
聊城: [115.97, 36.45],
|
||||
芜湖: [118.38, 31.33],
|
||||
唐山: [118.02, 39.63],
|
||||
平顶山: [113.29, 33.75],
|
||||
邢台: [114.48, 37.05],
|
||||
德州: [116.29, 37.45],
|
||||
济宁: [116.59, 35.38],
|
||||
荆州: [112.239741, 30.335165],
|
||||
宜昌: [111.3, 30.7],
|
||||
义乌: [120.06, 29.32],
|
||||
丽水: [119.92, 28.45],
|
||||
洛阳: [112.44, 34.7],
|
||||
秦皇岛: [119.57, 39.95],
|
||||
株洲: [113.16, 27.83],
|
||||
石家庄: [114.48, 38.03],
|
||||
莱芜: [117.67, 36.19],
|
||||
常德: [111.69, 29.05],
|
||||
保定: [115.48, 38.85],
|
||||
湘潭: [112.91, 27.87],
|
||||
金华: [119.64, 29.12],
|
||||
岳阳: [113.09, 29.37],
|
||||
长沙: [113, 28.21],
|
||||
衢州: [118.88, 28.97],
|
||||
廊坊: [116.7, 39.53],
|
||||
菏泽: [115.480656, 35.23375],
|
||||
合肥: [117.27, 31.86],
|
||||
武汉: [114.31, 30.52],
|
||||
大庆: [125.03, 46.58],
|
||||
};
|
||||
|
||||
const convertData = function(data: any) {
|
||||
let res = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let geoCoord = (geoCoordMap as any)[data[i].name];
|
||||
if (geoCoord) {
|
||||
res.push({
|
||||
name: data[i].name,
|
||||
value: geoCoord.concat(data[i].value),
|
||||
});
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const option = {
|
||||
backgroundColor: '#404a59',
|
||||
title: {
|
||||
text: '全国主要城市空气质量',
|
||||
subtext: 'data from PM25.in',
|
||||
sublink: 'http://www.pm25.in',
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
y: 'bottom',
|
||||
x: 'right',
|
||||
data: ['pm2.5'],
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
geo: {
|
||||
map: 'china',
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
roam: true,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#323c48',
|
||||
borderColor: '#111',
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: '#2a333d',
|
||||
},
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'pm2.5',
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
data: convertData(data),
|
||||
symbolSize: function(val: any) {
|
||||
return val[2] / 10;
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
formatter: '{b}',
|
||||
position: 'right',
|
||||
show: false,
|
||||
},
|
||||
emphasis: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#ddb926',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Top 5',
|
||||
type: 'effectScatter',
|
||||
coordinateSystem: 'geo',
|
||||
data: convertData(
|
||||
data
|
||||
.sort(function(a, b) {
|
||||
return b.value - a.value;
|
||||
})
|
||||
.slice(0, 6)
|
||||
),
|
||||
symbolSize: function(val: any) {
|
||||
return val[2] / 10;
|
||||
},
|
||||
showEffectOn: 'render',
|
||||
rippleEffect: {
|
||||
brushType: 'stroke',
|
||||
},
|
||||
hoverAnimation: true,
|
||||
label: {
|
||||
normal: {
|
||||
formatter: '{b}',
|
||||
position: 'right',
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#f4e925',
|
||||
shadowBlur: 10,
|
||||
shadowColor: '#333',
|
||||
},
|
||||
},
|
||||
zlevel: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
class EchartsEffectScatter extends Component {
|
||||
render() {
|
||||
return (
|
||||
<ReactEcharts
|
||||
option={option}
|
||||
style={{ height: '400px', width: '100%' }}
|
||||
className={'react_for_echarts'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EchartsEffectScatter;
|
||||
247
src/components/charts/EchartsForce.tsx
Normal file
247
src/components/charts/EchartsForce.tsx
Normal file
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* Created by SEELE on 2017/8/23.
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '',
|
||||
},
|
||||
tooltip: {},
|
||||
animationDurationUpdate: 1500,
|
||||
animationEasingUpdate: 'quinticInOut',
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
x: 'center',
|
||||
show: false,
|
||||
data: ['朋友', '战友', '亲戚'],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'graph',
|
||||
layout: 'force',
|
||||
symbolSize: 45,
|
||||
focusNodeAdjacency: true,
|
||||
roam: true,
|
||||
categories: [
|
||||
{
|
||||
name: '朋友',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#009800',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '战友',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#4592FF',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '亲戚',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#3592F',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
force: {
|
||||
repulsion: 1000,
|
||||
},
|
||||
edgeSymbolSize: [4, 50],
|
||||
edgeLabel: {
|
||||
normal: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: 10,
|
||||
},
|
||||
formatter: '{c}',
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{
|
||||
name: '徐贱云',
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '冯可梁',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '邓志荣',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '李荣庆',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '郑志勇',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '赵英杰',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '王承军',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '陈卫东',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '邹劲松',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '赵成',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '陈现忠',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '陶泳',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
name: '王德福',
|
||||
category: 1,
|
||||
draggable: true,
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
source: 0,
|
||||
target: 1,
|
||||
category: 0,
|
||||
value: '朋友',
|
||||
},
|
||||
{
|
||||
source: 0,
|
||||
target: 2,
|
||||
value: '战友',
|
||||
},
|
||||
{
|
||||
source: 0,
|
||||
target: 3,
|
||||
value: '房东',
|
||||
},
|
||||
{
|
||||
source: 0,
|
||||
target: 4,
|
||||
value: '朋友',
|
||||
},
|
||||
{
|
||||
source: 1,
|
||||
target: 2,
|
||||
value: '表亲',
|
||||
},
|
||||
{
|
||||
source: 0,
|
||||
target: 5,
|
||||
value: '朋友',
|
||||
},
|
||||
{
|
||||
source: 4,
|
||||
target: 5,
|
||||
value: '姑姑',
|
||||
},
|
||||
{
|
||||
source: 2,
|
||||
target: 8,
|
||||
value: '叔叔',
|
||||
},
|
||||
{
|
||||
source: 0,
|
||||
target: 12,
|
||||
value: '朋友',
|
||||
},
|
||||
{
|
||||
source: 6,
|
||||
target: 11,
|
||||
value: '爱人',
|
||||
},
|
||||
{
|
||||
source: 6,
|
||||
target: 3,
|
||||
value: '朋友',
|
||||
},
|
||||
{
|
||||
source: 7,
|
||||
target: 5,
|
||||
value: '朋友',
|
||||
},
|
||||
{
|
||||
source: 9,
|
||||
target: 10,
|
||||
value: '朋友',
|
||||
},
|
||||
{
|
||||
source: 3,
|
||||
target: 10,
|
||||
value: '朋友',
|
||||
},
|
||||
{
|
||||
source: 2,
|
||||
target: 11,
|
||||
value: '同学',
|
||||
},
|
||||
],
|
||||
lineStyle: {
|
||||
normal: {
|
||||
opacity: 0.9,
|
||||
width: 1,
|
||||
curveness: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
class EchartsForce extends Component {
|
||||
render() {
|
||||
return (
|
||||
<ReactEcharts
|
||||
option={option}
|
||||
style={{ height: '400px', width: '100%' }}
|
||||
className={'react_for_echarts'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EchartsForce;
|
||||
85
src/components/charts/EchartsGraphnpm.tsx
Normal file
85
src/components/charts/EchartsGraphnpm.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/21.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import { npmDependencies } from '../../service';
|
||||
|
||||
class EchartsGraphnpm extends React.Component {
|
||||
state = {
|
||||
option: {
|
||||
title: {
|
||||
text: 'NPM Dependencies',
|
||||
},
|
||||
animationDurationUpdate: 1500,
|
||||
animationEasingUpdate: 'quinticInOut',
|
||||
series: [
|
||||
{
|
||||
type: 'graph',
|
||||
layout: 'none',
|
||||
// progressiveThreshold: 700,
|
||||
data: [],
|
||||
edges: [],
|
||||
label: {
|
||||
emphasis: {
|
||||
position: 'right',
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
roam: true,
|
||||
focusNodeAdjacency: true,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 0.5,
|
||||
curveness: 0.3,
|
||||
opacity: 0.7,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
componentDidMount() {
|
||||
npmDependencies().then((npm) => {
|
||||
this.setState({
|
||||
option: {
|
||||
series: [
|
||||
{
|
||||
data: npm.nodes.map(function (node: any) {
|
||||
return {
|
||||
x: node.x,
|
||||
y: node.y,
|
||||
id: node.id,
|
||||
name: node.label,
|
||||
symbolSize: node.size,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: node.color,
|
||||
},
|
||||
},
|
||||
};
|
||||
}),
|
||||
edges: npm.edges.map(function (edge: any) {
|
||||
return {
|
||||
source: edge.sourceID,
|
||||
target: edge.targetID,
|
||||
};
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<ReactEcharts
|
||||
option={this.state.option}
|
||||
style={{ height: '300px', width: '100%' }}
|
||||
className={'react_for_echarts'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EchartsGraphnpm;
|
||||
88
src/components/charts/EchartsPie.tsx
Normal file
88
src/components/charts/EchartsPie.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/21.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: 'Customized Pie',
|
||||
left: 'center',
|
||||
top: 20,
|
||||
textStyle: {
|
||||
color: '#777',
|
||||
},
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)',
|
||||
},
|
||||
|
||||
visualMap: {
|
||||
show: false,
|
||||
min: 80,
|
||||
max: 600,
|
||||
inRange: {
|
||||
colorLightness: [0, 1],
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '访问来源',
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
center: ['50%', '50%'],
|
||||
data: [
|
||||
{ value: 335, name: '直接访问' },
|
||||
{ value: 310, name: '邮件营销' },
|
||||
{ value: 274, name: '联盟广告' },
|
||||
{ value: 235, name: '视频广告' },
|
||||
{ value: 400, name: '搜索引擎' },
|
||||
].sort(function(a, b) {
|
||||
return a.value - b.value;
|
||||
}),
|
||||
roseType: 'angle',
|
||||
label: {
|
||||
normal: {
|
||||
textStyle: {
|
||||
color: '#777',
|
||||
},
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
normal: {
|
||||
lineStyle: {
|
||||
color: '#777',
|
||||
},
|
||||
smooth: 0.2,
|
||||
length: 10,
|
||||
length2: 20,
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#c23531',
|
||||
shadowBlur: 200,
|
||||
shadowColor: '#777',
|
||||
},
|
||||
},
|
||||
|
||||
animationType: 'scale',
|
||||
animationEasing: 'elasticOut',
|
||||
animationDelay: function(idx: any) {
|
||||
return Math.random() * 200;
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const EchartsPie = () => (
|
||||
<ReactEcharts
|
||||
option={option}
|
||||
style={{ height: '300px', width: '100%' }}
|
||||
className={'react_for_echarts'}
|
||||
/>
|
||||
);
|
||||
|
||||
export default EchartsPie;
|
||||
138
src/components/charts/EchartsScatter.tsx
Normal file
138
src/components/charts/EchartsScatter.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/21.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import { weibo } from '../../service';
|
||||
require('echarts/map/js/china.js');
|
||||
|
||||
class EchartsScatter extends React.Component {
|
||||
state = {
|
||||
option: {
|
||||
backgroundColor: '#404a59',
|
||||
title: {
|
||||
text: '微博签到数据点亮中国',
|
||||
subtext: 'From ThinkGIS',
|
||||
sublink: 'http://www.thinkgis.cn/public/sina',
|
||||
left: 'center',
|
||||
top: 'top',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
tooltip: {},
|
||||
legend: {
|
||||
left: 'left',
|
||||
data: ['强', '中', '弱'],
|
||||
textStyle: {
|
||||
color: '#ccc',
|
||||
},
|
||||
},
|
||||
geo: {
|
||||
map: 'china',
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#323c48',
|
||||
borderColor: '#111',
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: '#2a333d',
|
||||
},
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '弱',
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
symbolSize: 1,
|
||||
large: true,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
shadowBlur: 2,
|
||||
shadowColor: 'rgba(37, 140, 249, 0.8)',
|
||||
color: 'rgba(37, 140, 249, 0.8)',
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
},
|
||||
{
|
||||
name: '中',
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
symbolSize: 1,
|
||||
large: true,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
shadowBlur: 2,
|
||||
shadowColor: 'rgba(14, 241, 242, 0.8)',
|
||||
color: 'rgba(14, 241, 242, 0.8)',
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
},
|
||||
{
|
||||
name: '强',
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo',
|
||||
symbolSize: 1,
|
||||
large: true,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
shadowBlur: 2,
|
||||
shadowColor: 'rgba(255, 255, 255, 0.8)',
|
||||
color: 'rgba(255, 255, 255, 0.8)',
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
componentDidMount() {
|
||||
weibo().then((weiboData) => {
|
||||
weiboData = weiboData.map(function (serieData: any) {
|
||||
var px = serieData[0] / 1000;
|
||||
var py = serieData[1] / 1000;
|
||||
var res = [[px, py]];
|
||||
|
||||
for (var i = 2; i < serieData.length; i += 2) {
|
||||
var dx = serieData[i] / 1000;
|
||||
var dy = serieData[i + 1] / 1000;
|
||||
var x = px + dx;
|
||||
var y = py + dy;
|
||||
res.push([parseInt(x.toFixed(2), 10), parseInt(y.toFixed(2), 10), 1]);
|
||||
|
||||
px = x;
|
||||
py = y;
|
||||
}
|
||||
return res;
|
||||
});
|
||||
this.setState({
|
||||
option: {
|
||||
series: [
|
||||
{ data: weiboData[0] },
|
||||
{ data: weiboData[1] },
|
||||
{ data: weiboData[2] },
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<ReactEcharts
|
||||
option={this.state.option}
|
||||
style={{ height: '400px', width: '100%' }}
|
||||
className={'react_for_echarts'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EchartsScatter;
|
||||
54
src/components/charts/Recharts.tsx
Normal file
54
src/components/charts/Recharts.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/21.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import RechartsSimpleLineChart from './RechartsSimpleLineChart';
|
||||
import RechartsBarChart from './RechartsBarChart';
|
||||
import RechartsRadialBarChart from './RechartsRadialBarChart';
|
||||
import RechartsRadarChart from './RechartsRadarChart';
|
||||
|
||||
class Recharts extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="gutter-example">
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="基础线形图" bordered={false}>
|
||||
<RechartsSimpleLineChart />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="基础线形图" bordered={false}>
|
||||
<RechartsBarChart />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card title="基础线形图" bordered={false}>
|
||||
<RechartsRadialBarChart />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card title="基础线形图" bordered={false}>
|
||||
<RechartsRadarChart />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Recharts;
|
||||
34
src/components/charts/RechartsBarChart.tsx
Normal file
34
src/components/charts/RechartsBarChart.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/21.
|
||||
*/
|
||||
import React from 'react';
|
||||
import {BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts';
|
||||
|
||||
const data = [
|
||||
{name: 'Page A', uv: 4000, pv: 2400, amt: 2400},
|
||||
{name: 'Page B', uv: 3000, pv: 1398, amt: 2210},
|
||||
{name: 'Page C', uv: 2000, pv: 9800, amt: 2290},
|
||||
{name: 'Page D', uv: 2780, pv: 3908, amt: 2000},
|
||||
{name: 'Page E', uv: 1890, pv: 4800, amt: 2181},
|
||||
{name: 'Page F', uv: 2390, pv: 3800, amt: 2500},
|
||||
{name: 'Page G', uv: 3490, pv: 4300, amt: 2100},
|
||||
];
|
||||
|
||||
const RechartsBarChart = () => (
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart
|
||||
data={data}
|
||||
margin={{top: 5, right: 30, left: 20, bottom: 5}}
|
||||
>
|
||||
<XAxis dataKey="name" />
|
||||
<YAxis />
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Bar dataKey="pv" fill="#8884d8" />
|
||||
<Bar dataKey="uv" fill="#82ca9d" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
|
||||
export default RechartsBarChart;
|
||||
30
src/components/charts/RechartsRadarChart.tsx
Normal file
30
src/components/charts/RechartsRadarChart.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/22.
|
||||
*/
|
||||
import React from 'react';
|
||||
import {Radar, RadarChart, PolarGrid, Legend,
|
||||
PolarAngleAxis, PolarRadiusAxis, ResponsiveContainer} from 'recharts';
|
||||
|
||||
const data = [
|
||||
{ subject: 'Math', A: 120, B: 110, fullMark: 150 },
|
||||
{ subject: 'Chinese', A: 98, B: 130, fullMark: 150 },
|
||||
{ subject: 'English', A: 86, B: 130, fullMark: 150 },
|
||||
{ subject: 'Geography', A: 99, B: 100, fullMark: 150 },
|
||||
{ subject: 'Physics', A: 85, B: 90, fullMark: 150 },
|
||||
{ subject: 'History', A: 65, B: 85, fullMark: 150 },
|
||||
];
|
||||
|
||||
const RechartsRadarChart = () => (
|
||||
<ResponsiveContainer width="100%" height={300} >
|
||||
<RadarChart outerRadius={90} data={data}>
|
||||
<Radar name="Mike" dataKey="A" stroke="#8884d8" fill="#8884d8" fillOpacity={0.6} />
|
||||
<Radar name="Lily" dataKey="B" stroke="#82ca9d" fill="#82ca9d" fillOpacity={0.6} />
|
||||
<PolarGrid />
|
||||
<Legend />
|
||||
<PolarAngleAxis dataKey="subject" />
|
||||
<PolarRadiusAxis angle={30} domain={[0, 150]} />
|
||||
</RadarChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
|
||||
export default RechartsRadarChart;
|
||||
42
src/components/charts/RechartsRadialBarChart.tsx
Normal file
42
src/components/charts/RechartsRadialBarChart.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/22.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { RadialBarChart, Legend, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
|
||||
const data = [
|
||||
{ name: '18-24', uv: 31.47, pv: 2400, fill: '#8884d8' },
|
||||
{ name: '25-29', uv: 26.69, pv: 4567, fill: '#83a6ed' },
|
||||
{ name: '30-34', uv: 15.69, pv: 1398, fill: '#8dd1e1' },
|
||||
{ name: '35-39', uv: 8.22, pv: 9800, fill: '#82ca9d' },
|
||||
{ name: '40-49', uv: 8.63, pv: 3908, fill: '#a4de6c' },
|
||||
{ name: '50+', uv: 2.63, pv: 4800, fill: '#d0ed57' },
|
||||
{ name: 'unknow', uv: 6.67, pv: 4800, fill: '#ffc658' },
|
||||
];
|
||||
|
||||
const RechartsRadialBarChart = () => (
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<RadialBarChart width={730} height={250} innerRadius="10%" outerRadius="80%" data={data}>
|
||||
{/* <RadialBar
|
||||
startAngle={90}
|
||||
endAngle={-270}
|
||||
minAngle={15}
|
||||
label
|
||||
background
|
||||
clockWise
|
||||
dataKey="uv"
|
||||
/> */}
|
||||
<Legend
|
||||
iconSize={10}
|
||||
width={120}
|
||||
height={140}
|
||||
layout="vertical"
|
||||
verticalAlign="middle"
|
||||
align="right"
|
||||
/>
|
||||
<Tooltip />
|
||||
</RadialBarChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
|
||||
export default RechartsRadialBarChart;
|
||||
36
src/components/charts/RechartsSimpleLineChart.tsx
Normal file
36
src/components/charts/RechartsSimpleLineChart.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/21.
|
||||
*/
|
||||
import React from 'react';
|
||||
import {LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts';
|
||||
|
||||
|
||||
const data = [
|
||||
{name: 'Page A', uv: 4000, pv: 2400, amt: 2400},
|
||||
{name: 'Page B', uv: 3000, pv: 1398, amt: 2210},
|
||||
{name: 'Page C', uv: 2000, pv: 9800, amt: 2290},
|
||||
{name: 'Page D', uv: 2780, pv: 3908, amt: 2000},
|
||||
{name: 'Page E', uv: 1890, pv: 4800, amt: 2181},
|
||||
{name: 'Page F', uv: 2390, pv: 3800, amt: 2500},
|
||||
{name: 'Page G', uv: 3490, pv: 4300, amt: 2100},
|
||||
];
|
||||
|
||||
const RechartsSimpleLineChart = () => (
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<LineChart
|
||||
data={data}
|
||||
margin={{top: 5, right: 30, left: 20, bottom: 5}}
|
||||
>
|
||||
|
||||
<XAxis dataKey="name" />
|
||||
<YAxis />
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Line type="monotone" dataKey="pv" stroke="#8884d8" activeDot={{r: 8}} />
|
||||
<Line type="monotone" dataKey="uv" stroke="#82ca9d" />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
|
||||
export default RechartsSimpleLineChart;
|
||||
32
src/components/cssmodule/index.module.less
Normal file
32
src/components/cssmodule/index.module.less
Normal file
@@ -0,0 +1,32 @@
|
||||
@font-face {
|
||||
font-family: 'Monoton';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Monoton'), local('Monoton-Regular'), url(../../style/font/y6oxFxU60dYw9khW6q8jGw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
|
||||
}
|
||||
.header {
|
||||
font-size: 7em;
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
font-family: Monoton;
|
||||
p {
|
||||
animation: neon1 1.5s ease-in-out infinite alternate;
|
||||
&:hover {
|
||||
color: #FF1177;
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes neon1 {
|
||||
from {
|
||||
text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #fff, 0 0 40px #FF1177, 0 0 70px #FF1177, 0 0 80px #FF1177, 0 0 100px #FF1177, 0 0 150px #FF1177;
|
||||
}
|
||||
to {
|
||||
text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #fff, 0 0 20px #FF1177, 0 0 35px #FF1177, 0 0 40px #FF1177, 0 0 50px #FF1177, 0 0 75px #FF1177;
|
||||
}
|
||||
}
|
||||
31
src/components/cssmodule/index.tsx
Normal file
31
src/components/cssmodule/index.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
*
|
||||
* 添加注释
|
||||
* Created by SEELE on 2018/1/12
|
||||
*
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import { Col, Card, Row } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import styles from './index.module.less';
|
||||
|
||||
class Cssmodule extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<BreadcrumbCustom breads={['cssModule']} />
|
||||
<Row gutter={16}>
|
||||
<Col md={24}>
|
||||
<Card title="cssModule" bordered={false}>
|
||||
<div className={styles.header}>
|
||||
<p>Hello CssModule</p>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Cssmodule;
|
||||
204
src/components/dashboard/Dashboard.tsx
Normal file
204
src/components/dashboard/Dashboard.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/5/3.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card, Timeline } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import EchartsViews from './EchartsViews';
|
||||
import EchartsProjects from './EchartsProjects';
|
||||
import b1 from '../../style/imgs/b1.jpg';
|
||||
import {
|
||||
CameraOutlined,
|
||||
CloudOutlined,
|
||||
HeartOutlined,
|
||||
MailOutlined,
|
||||
SyncOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
class Dashboard extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom />
|
||||
<Row gutter={10}>
|
||||
<Col className="gutter-row" md={4}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<div className="clear y-center">
|
||||
<div className="pull-left mr-m">
|
||||
<HeartOutlined className="text-2x text-danger" />
|
||||
</div>
|
||||
<div className="clear">
|
||||
<div className="text-muted">收藏</div>
|
||||
<h2>301</h2>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<div className="clear y-center">
|
||||
<div className="pull-left mr-m">
|
||||
<CloudOutlined type="cloud" className="text-2x" />
|
||||
</div>
|
||||
<div className="clear">
|
||||
<div className="text-muted">云数据</div>
|
||||
<h2>30122</h2>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={4}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<div className="clear y-center">
|
||||
<div className="pull-left mr-m">
|
||||
<CameraOutlined className="text-2x text-info" />
|
||||
</div>
|
||||
<div className="clear">
|
||||
<div className="text-muted">照片</div>
|
||||
<h2>802</h2>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<div className="clear y-center">
|
||||
<div className="pull-left mr-m">
|
||||
<MailOutlined className="text-2x text-success" />
|
||||
</div>
|
||||
<div className="clear">
|
||||
<div className="text-muted">邮件</div>
|
||||
<h2>102</h2>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={16}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false} className={'no-padding'}>
|
||||
<EchartsProjects />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={10}>
|
||||
<Col className="gutter-row" md={8}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<div className="pb-m">
|
||||
<h3>任务</h3>
|
||||
<small>10个已经完成,2个待完成,1个正在进行中</small>
|
||||
</div>
|
||||
<span className="card-tool">
|
||||
<SyncOutlined />
|
||||
</span>
|
||||
<Timeline>
|
||||
<Timeline.Item color="green">新版本迭代会</Timeline.Item>
|
||||
<Timeline.Item color="green">完成网站设计初版</Timeline.Item>
|
||||
<Timeline.Item color="red">
|
||||
<p>联调接口</p>
|
||||
<p>功能验收</p>
|
||||
</Timeline.Item>
|
||||
|
||||
<Timeline.Item color="#108ee9">
|
||||
<p>登录功能设计</p>
|
||||
<p>权限验证</p>
|
||||
<p>页面排版</p>
|
||||
</Timeline.Item>
|
||||
</Timeline>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={8}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<div className="pb-m">
|
||||
<h3>消息栏</h3>
|
||||
</div>
|
||||
<span className="card-tool">
|
||||
<SyncOutlined />
|
||||
</span>
|
||||
<ul className="list-group no-border">
|
||||
<li className="list-group-item">
|
||||
<span className="pull-left w-40 mr-m">
|
||||
<img
|
||||
src={b1}
|
||||
className="img-responsive img-circle"
|
||||
alt="test"
|
||||
/>
|
||||
</span>
|
||||
<div className="clear">
|
||||
<span className="block">鸣人</span>
|
||||
<span className="text-muted">终于当上火影了!</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="list-group-item">
|
||||
<span className="pull-left w-40 mr-m">
|
||||
<img
|
||||
src={b1}
|
||||
className="img-responsive img-circle"
|
||||
alt="test"
|
||||
/>
|
||||
</span>
|
||||
<div className="clear">
|
||||
<span className="block">佐助</span>
|
||||
<span className="text-muted">吊车尾~~</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="list-group-item">
|
||||
<span className="pull-left w-40 mr-m">
|
||||
<img
|
||||
src={b1}
|
||||
className="img-responsive img-circle"
|
||||
alt="test"
|
||||
/>
|
||||
</span>
|
||||
<div className="clear">
|
||||
<span className="block">小樱</span>
|
||||
<span className="text-muted">佐助,你好帅!</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="list-group-item">
|
||||
<span className="pull-left w-40 mr-m">
|
||||
<img
|
||||
src={b1}
|
||||
className="img-responsive img-circle"
|
||||
alt="test"
|
||||
/>
|
||||
</span>
|
||||
<div className="clear">
|
||||
<span className="block">雏田</span>
|
||||
<span className="text-muted">
|
||||
鸣人君。。。那个。。。我。。喜欢你..
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={8}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<div className="pb-m">
|
||||
<h3>访问量统计</h3>
|
||||
<small>最近7天用户访问量</small>
|
||||
</div>
|
||||
<span className="card-tool">
|
||||
<SyncOutlined type="sync" />
|
||||
</span>
|
||||
<EchartsViews />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Dashboard;
|
||||
116
src/components/dashboard/EchartsProjects.tsx
Normal file
116
src/components/dashboard/EchartsProjects.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/5/5.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
|
||||
let xAxisData = [];
|
||||
let data = [];
|
||||
for (let i = 0; i < 50; i++) {
|
||||
xAxisData.push(i);
|
||||
data.push(Math.ceil((Math.cos(i / 5) * (i / 5) + i / 6) * 5) + 10);
|
||||
}
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '最近50天每天项目完成情况',
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
color: '#ccc',
|
||||
fontSize: 10
|
||||
}
|
||||
},
|
||||
backgroundColor: '#08263a',
|
||||
xAxis: [{
|
||||
show: true,
|
||||
data: xAxisData,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#ccc'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
show: false,
|
||||
data: xAxisData
|
||||
}],
|
||||
tooltip: {},
|
||||
visualMap: {
|
||||
show: false,
|
||||
min: 0,
|
||||
max: 50,
|
||||
dimension: 0,
|
||||
inRange: {
|
||||
color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055']
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#ccc'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#08263f'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Simulate Shadow',
|
||||
type: 'line',
|
||||
data: data,
|
||||
z: 2,
|
||||
showSymbol: false,
|
||||
animationDelay: 0,
|
||||
animationEasing: 'linear',
|
||||
animationDuration: 1200,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
color: 'transparent'
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: '#08263a',
|
||||
shadowBlur: 50,
|
||||
shadowColor: '#000'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: '完成项目数',
|
||||
type: 'bar',
|
||||
data: data,
|
||||
xAxisIndex: 1,
|
||||
z: 3,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
barBorderRadius: 5
|
||||
}
|
||||
}
|
||||
}],
|
||||
animationEasing: 'elasticOut',
|
||||
animationEasingUpdate: 'elasticOut',
|
||||
animationDelay: function (idx: number) {
|
||||
return idx * 20;
|
||||
},
|
||||
animationDelayUpdate: function (idx: number) {
|
||||
return idx * 20;
|
||||
}
|
||||
};
|
||||
const EchartsProjects = () => (
|
||||
<ReactEcharts
|
||||
option={option}
|
||||
style={{height: '212px', width: '100%'}}
|
||||
className={'react_for_echarts'}
|
||||
/>
|
||||
);
|
||||
|
||||
export default EchartsProjects;
|
||||
121
src/components/dashboard/EchartsViews.tsx
Normal file
121
src/components/dashboard/EchartsViews.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/5/5.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import echarts from 'echarts';
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '最近7天用户访问量',
|
||||
left: '50%',
|
||||
show: false,
|
||||
textAlign: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
lineStyle: {
|
||||
color: '#ddd'
|
||||
}
|
||||
},
|
||||
backgroundColor: 'rgba(255,255,255,1)',
|
||||
padding: [5, 10],
|
||||
textStyle: {
|
||||
color: '#7588E4',
|
||||
},
|
||||
extraCssText: 'box-shadow: 0 0 5px rgba(0,0,0,0.3)'
|
||||
},
|
||||
legend: {
|
||||
right: 20,
|
||||
orient: 'vertical',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['2017-05-01', '2017-05-02', '2017-05-03', '2017-05-04', '2017-05-05', '2017-05-06','2017-05-07'],
|
||||
boundaryGap: false,
|
||||
splitLine: {
|
||||
show: true,
|
||||
interval: 'auto',
|
||||
lineStyle: {
|
||||
color: ['#D4DFF5']
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#609ee9'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
margin: 10,
|
||||
textStyle: {
|
||||
fontSize: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: ['#D4DFF5']
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#609ee9'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
margin: 0,
|
||||
textStyle: {
|
||||
fontSize: 8
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: '昨日',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
data: ['1200', '1400', '808', '811', '626', '488', '1600'],
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: 'rgba(216, 244, 247,1)'
|
||||
}, {
|
||||
offset: 1,
|
||||
color: 'rgba(216, 244, 247,1)'
|
||||
}], false)
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#58c8da'
|
||||
}
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 3
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
const EchartsViews = () => (
|
||||
<ReactEcharts
|
||||
option={option}
|
||||
style={{height: '350px', width: '100%'}}
|
||||
className={'react_for_echarts'}
|
||||
/>
|
||||
);
|
||||
|
||||
export default EchartsViews;
|
||||
37
src/components/env/index.tsx
vendored
Normal file
37
src/components/env/index.tsx
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* File: index.tsx
|
||||
* Desc: 环境配置
|
||||
* File Created: 2020-08-02 23:00:28
|
||||
* Author: yezi
|
||||
* ------
|
||||
* Copyright 2020 - present, yezi
|
||||
*/
|
||||
import React from 'react';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { Row, Col, Card, Descriptions } from 'antd';
|
||||
|
||||
const getEnvs = () => Object.keys(process.env).filter((key) => /^REACT_ADMIN_/i.test(key));
|
||||
const Env = () => {
|
||||
const envs = getEnvs();
|
||||
console.log(process.env);
|
||||
return (
|
||||
<div>
|
||||
<BreadcrumbCustom breads={['环境变量配置']} />
|
||||
<Row gutter={16}>
|
||||
<Col md={24}>
|
||||
<Card title="环境变量配置" bordered={false}>
|
||||
<Descriptions>
|
||||
{envs.map((env) => (
|
||||
<Descriptions.Item key={env} label={env}>
|
||||
{process.env[env]}
|
||||
</Descriptions.Item>
|
||||
))}
|
||||
</Descriptions>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Env;
|
||||
29
src/components/extension/MultipleMenu.tsx
Normal file
29
src/components/extension/MultipleMenu.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* File: MultipleMenu.tsx
|
||||
* Desc: 多级菜单组件
|
||||
* File Created: 2019-12-18 23:15:35
|
||||
* Author: chenghao
|
||||
* ------
|
||||
* Copyright 2019 - present, karakal
|
||||
*/
|
||||
import React from 'react';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
|
||||
const MultipleMenu = () => {
|
||||
return (
|
||||
<div>
|
||||
<BreadcrumbCustom breads={['多级菜单']} />
|
||||
<Row gutter={16}>
|
||||
<Col md={24}>
|
||||
<Card title="多级菜单" bordered={false}>
|
||||
<div>多级菜单的功能扩展</div>
|
||||
<div>菜单样式可能需要你来调整</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MultipleMenu;
|
||||
43
src/components/extension/QueryParams.tsx
Normal file
43
src/components/extension/QueryParams.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* File: QueryParams.js
|
||||
* Desc: query参数demo
|
||||
* File Created: 2018-11-25 23:18:09
|
||||
* Author: chenghao
|
||||
* Copyright 2018 - present, chenghao
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
|
||||
type QueryParamsProps = {
|
||||
query: any;
|
||||
};
|
||||
|
||||
class QueryParams extends Component<QueryParamsProps> {
|
||||
render() {
|
||||
const { query } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<BreadcrumbCustom breads={['queryParams']} />
|
||||
<Row gutter={16}>
|
||||
<Col md={24}>
|
||||
<Card title="query参数Demo" bordered={false}>
|
||||
<div>参数1: {query.param1}</div>
|
||||
<div>参数2: {query.param2}</div>
|
||||
<div>
|
||||
其他参数:{' '}
|
||||
{query.others || (
|
||||
<a href="#/app/extension/queryParams?others=nothing">
|
||||
点击查看
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default QueryParams;
|
||||
37
src/components/extension/Visitor.tsx
Normal file
37
src/components/extension/Visitor.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* File: Visitor.tsx
|
||||
* Desc: 访客
|
||||
* File Created: 2019-10-25 22:31:37
|
||||
* Author: chenghao
|
||||
* ------
|
||||
* Copyright 2019 - present, chenghao
|
||||
*/
|
||||
import React from 'react';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
|
||||
const Visitor = () => {
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbCustom breads={['visitor']} />
|
||||
<Row gutter={16}>
|
||||
<Col md={24}>
|
||||
<Card
|
||||
title="访客模式"
|
||||
bordered={false}
|
||||
bodyStyle={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: 500,
|
||||
}}
|
||||
>
|
||||
访客模式的页面,你不需要登录即可访问的页面
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Visitor;
|
||||
70
src/components/index.tsx
Normal file
70
src/components/index.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 路由组件出口文件
|
||||
* yezi 2018年6月24日
|
||||
*/
|
||||
import Loadable from 'react-loadable';
|
||||
import Loading from './widget/Loading';
|
||||
import BasicTable from './tables/BasicTables';
|
||||
import AdvancedTable from './tables/AdvancedTables';
|
||||
import AsynchronousTable from './tables/AsynchronousTable';
|
||||
import Echarts from './charts/Echarts';
|
||||
import Recharts from './charts/Recharts';
|
||||
import Icons from './ui/Icons';
|
||||
import Buttons from './ui/Buttons';
|
||||
import Spins from './ui/Spins';
|
||||
import Modals from './ui/Modals';
|
||||
import Notifications from './ui/Notifications';
|
||||
import Tabs from './ui/Tabs';
|
||||
import Banners from './ui/banners';
|
||||
import Drags from './ui/Draggable';
|
||||
import Dashboard from './dashboard/Dashboard';
|
||||
import Gallery from './ui/Gallery';
|
||||
import BasicAnimations from './animation/BasicAnimations';
|
||||
import ExampleAnimations from './animation/ExampleAnimations';
|
||||
import AuthBasic from './auth/Basic';
|
||||
import RouterEnter from './auth/RouterEnter';
|
||||
import Cssmodule from './cssmodule';
|
||||
import MapUi from './ui/map';
|
||||
import QueryParams from './extension/QueryParams';
|
||||
import Visitor from './extension/Visitor';
|
||||
import MultipleMenu from './extension/MultipleMenu';
|
||||
import Sub1 from './smenu/Sub1';
|
||||
import Sub2 from './smenu/Sub2';
|
||||
import Env from './env';
|
||||
|
||||
const WysiwygBundle = Loadable({
|
||||
// 按需加载富文本配置
|
||||
loader: () => import('./ui/Wysiwyg'),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
export default {
|
||||
BasicTable,
|
||||
AdvancedTable,
|
||||
AsynchronousTable,
|
||||
Echarts,
|
||||
Recharts,
|
||||
Icons,
|
||||
Buttons,
|
||||
Spins,
|
||||
Modals,
|
||||
Notifications,
|
||||
Tabs,
|
||||
Banners,
|
||||
Drags,
|
||||
Dashboard,
|
||||
Gallery,
|
||||
BasicAnimations,
|
||||
ExampleAnimations,
|
||||
AuthBasic,
|
||||
RouterEnter,
|
||||
WysiwygBundle,
|
||||
Cssmodule,
|
||||
MapUi,
|
||||
QueryParams,
|
||||
Visitor,
|
||||
MultipleMenu,
|
||||
Sub1,
|
||||
Sub2,
|
||||
Env,
|
||||
} as any;
|
||||
113
src/components/pages/Login.tsx
Normal file
113
src/components/pages/Login.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/16.
|
||||
*/
|
||||
import React, { useEffect } from 'react';
|
||||
import { Button, Form, Input } from 'antd';
|
||||
import { PwaInstaller } from '../widget';
|
||||
import { useAlita } from 'redux-alita';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { FormProps } from 'antd/lib/form';
|
||||
import umbrella from 'umbrella-storage';
|
||||
import { GithubOutlined, LockOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import { useUpdateEffect } from 'ahooks';
|
||||
import { trailwayLogin } from '../../service';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
type LoginProps = {
|
||||
setAlitaState: (param: any) => void;
|
||||
auth: any;
|
||||
} & RouteComponentProps &
|
||||
FormProps;
|
||||
|
||||
const Login = (props: LoginProps) => {
|
||||
const { history } = props;
|
||||
const [auth, setAlita] = useAlita({ auth: {} }, { light: true });
|
||||
|
||||
useEffect(() => {
|
||||
setAlita('auth', null);
|
||||
}, [setAlita]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
if (auth && auth.uid) {
|
||||
// 判断是否登陆
|
||||
umbrella.setLocalStorage('user', auth);
|
||||
history.push('/');
|
||||
}
|
||||
}, [history, auth]);
|
||||
|
||||
const handleSubmit = (values: any) => {
|
||||
if (checkUser(values)) {
|
||||
setAlita({ funcName: values.userName, stateName: 'auth' });
|
||||
}
|
||||
};
|
||||
const checkUser = (values: any) => {
|
||||
// const users = [
|
||||
// ['admin', 'admin'],
|
||||
// ['guest', 'guest'],
|
||||
// ];
|
||||
// return users.some((user) => user[0] === values.userName && user[1] === values.password);
|
||||
let success = trailwayLogin({ username: values.userName, password: values.password }).then(
|
||||
(res) => {
|
||||
console.log(res);
|
||||
return res.data.success === true;
|
||||
}
|
||||
);
|
||||
console.log(success);
|
||||
return success;
|
||||
};
|
||||
const gitHub = () => {
|
||||
window.location.href =
|
||||
'https://github.com/login/oauth/authorize?client_id=792cdcd244e98dcd2dee&redirect_uri=http://localhost:3006/&scope=user&state=reactAdmin';
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="login">
|
||||
<div className="login-form">
|
||||
<div className="login-logo">
|
||||
<span>React Admin</span>
|
||||
<PwaInstaller />
|
||||
</div>
|
||||
<Form onFinish={handleSubmit} style={{ maxWidth: '300px' }}>
|
||||
<FormItem
|
||||
name="userName"
|
||||
rules={[{ required: true, message: '请输入用户名!' }]}
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined size={13} />}
|
||||
placeholder="管理员输入admin, 游客输入guest"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem name="password" rules={[{ required: true, message: '请输入密码!' }]}>
|
||||
<Input
|
||||
prefix={<LockOutlined size={13} />}
|
||||
type="password"
|
||||
placeholder="管理员输入admin, 游客输入guest"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<span className="login-form-forgot" style={{ float: 'right' }}>
|
||||
忘记密码
|
||||
</span>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
className="login-form-button"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
登录
|
||||
</Button>
|
||||
<p style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>或 现在就去注册!</span>
|
||||
<span onClick={gitHub}>
|
||||
<GithubOutlined />
|
||||
(第三方登录)
|
||||
</span>
|
||||
</p>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
31
src/components/pages/NotFound.tsx
Normal file
31
src/components/pages/NotFound.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/5/7.
|
||||
*/
|
||||
import React from 'react';
|
||||
import img from '../../style/imgs/404.png';
|
||||
|
||||
class NotFound extends React.Component {
|
||||
state = {
|
||||
animated: '',
|
||||
};
|
||||
enter = () => {
|
||||
this.setState({ animated: 'hinge' });
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className="center"
|
||||
style={{ height: '100%', background: '#ececec', overflow: 'hidden' }}
|
||||
>
|
||||
<img
|
||||
src={img}
|
||||
alt="404"
|
||||
className={`animated swing ${this.state.animated}`}
|
||||
onMouseEnter={this.enter}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NotFound;
|
||||
28
src/components/smenu/Sub1.tsx
Normal file
28
src/components/smenu/Sub1.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* File: Sub1.tsx
|
||||
* Desc: 异步子菜单
|
||||
* File Created: 2020-01-21 11:31:15
|
||||
* Author: chenghao at <865470087@qq.com>
|
||||
* ------
|
||||
* Copyright 2020 - present, chenghao
|
||||
*/
|
||||
import React from 'react';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
|
||||
const SmenuSub1 = () => {
|
||||
return (
|
||||
<div>
|
||||
<BreadcrumbCustom breads={['异步菜单']} />
|
||||
<Row gutter={16}>
|
||||
<Col md={24}>
|
||||
<Card title="异步子菜单" bordered={false}>
|
||||
<div>异步子菜单1</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SmenuSub1;
|
||||
28
src/components/smenu/Sub2.tsx
Normal file
28
src/components/smenu/Sub2.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* File: Sub2.tsx
|
||||
* Desc: 异步子菜单
|
||||
* File Created: 2020-01-21 11:31:15
|
||||
* Author: chenghao at <865470087@qq.com>
|
||||
* ------
|
||||
* Copyright 2020 - present, chenghao
|
||||
*/
|
||||
import React from 'react';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
|
||||
const SmenuSub2 = () => {
|
||||
return (
|
||||
<div>
|
||||
<BreadcrumbCustom breads={['异步菜单']} />
|
||||
<Row gutter={16}>
|
||||
<Col md={24}>
|
||||
<Card title="异步子菜单" bordered={false}>
|
||||
<div>异步子菜单2</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SmenuSub2;
|
||||
45
src/components/tables/AdvancedTables.tsx
Normal file
45
src/components/tables/AdvancedTables.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/16.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import FixedTable from './FixedTable';
|
||||
import ExpandedTable from './ExpandedTable';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
|
||||
class AdvancedTables extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="gutter-example">
|
||||
<BreadcrumbCustom breads={['表格', '高级表格']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="固定列" bordered={false}>
|
||||
<FixedTable />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card title="可展开" bordered={false}>
|
||||
<ExpandedTable />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
{/* <Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card title="可编辑" bordered={false}>
|
||||
<EditableTable />
|
||||
</Card>
|
||||
</div>
|
||||
</Col> */}
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AdvancedTables;
|
||||
102
src/components/tables/AsynchronousTable.tsx
Normal file
102
src/components/tables/AsynchronousTable.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/16.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Table, Button, Row, Col, Card } from 'antd';
|
||||
import { getBbcNews } from '../../service';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '新闻标题',
|
||||
dataIndex: 'title',
|
||||
width: 100,
|
||||
render: (text: any, record: any) => (
|
||||
<a href={record.url} target="_blank" rel="noopener noreferrer">
|
||||
{text}
|
||||
</a>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '作者',
|
||||
dataIndex: 'author',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '发布时间',
|
||||
dataIndex: 'publishedAt',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
class AsynchronousTable extends React.Component {
|
||||
state = {
|
||||
selectedRowKeys: [], // Check here to configure the default column
|
||||
loading: false,
|
||||
data: [],
|
||||
};
|
||||
componentDidMount() {
|
||||
this.start();
|
||||
}
|
||||
start = () => {
|
||||
this.setState({ loading: true });
|
||||
getBbcNews().then(({ articles }: { articles: any }) => {
|
||||
this.setState({
|
||||
data: articles,
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
onSelectChange = (selectedRowKeys: string[]) => {
|
||||
console.log('selectedRowKeys changed: ', selectedRowKeys);
|
||||
this.setState({ selectedRowKeys });
|
||||
};
|
||||
render() {
|
||||
const { loading, selectedRowKeys } = this.state;
|
||||
const rowSelection = {
|
||||
selectedRowKeys,
|
||||
onChange: this.onSelectChange,
|
||||
};
|
||||
const hasSelected = selectedRowKeys.length > 0;
|
||||
return (
|
||||
<div className="gutter-example">
|
||||
<BreadcrumbCustom breads={['表格', '异步表格']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="异步表格--BBC新闻今日热门" bordered={false}>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={this.start}
|
||||
disabled={loading}
|
||||
loading={loading}
|
||||
>
|
||||
Reload
|
||||
</Button>
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
{hasSelected
|
||||
? `Selected ${selectedRowKeys.length} items`
|
||||
: ''}
|
||||
</span>
|
||||
</div>
|
||||
<Table
|
||||
rowSelection={rowSelection as any}
|
||||
columns={columns}
|
||||
dataSource={this.state.data}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AsynchronousTable;
|
||||
65
src/components/tables/BasicTable.tsx
Normal file
65
src/components/tables/BasicTable.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/15.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Table, Button } from 'antd';
|
||||
import { DownOutlined } from '@ant-design/icons';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (text: any) => <span>{text}</span>,
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'action',
|
||||
render: (text: any, record: any) => (
|
||||
<span>
|
||||
<Button>Action 一 {record.name}</Button>
|
||||
<span className="ant-divider" />
|
||||
<Button>Delete</Button>
|
||||
<span className="ant-divider" />
|
||||
<Button className="ant-dropdown-link">
|
||||
More actions <DownOutlined />
|
||||
</Button>
|
||||
</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const data = [
|
||||
{
|
||||
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',
|
||||
},
|
||||
];
|
||||
|
||||
const BasicTable = () => <Table columns={columns} dataSource={data} />;
|
||||
|
||||
export default BasicTable;
|
||||
52
src/components/tables/BasicTables.tsx
Normal file
52
src/components/tables/BasicTables.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/15.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import BasicTable from './BasicTable';
|
||||
import SelectTable from './SelectTable';
|
||||
import SortTable from './SortTable';
|
||||
import SearchTable from './SearchTable';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
|
||||
const BasicTables = () => (
|
||||
<div className="gutter-example">
|
||||
<BreadcrumbCustom breads={['表格', '基础表格']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="基础表格" bordered={false}>
|
||||
<BasicTable />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="基础表格" bordered={false}>
|
||||
<SelectTable />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card title="可控的筛选和排序" bordered={false}>
|
||||
<SortTable />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card title="自定义筛选" bordered={false}>
|
||||
<SearchTable />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default BasicTables;
|
||||
28
src/components/tables/ExpandedTable.tsx
Normal file
28
src/components/tables/ExpandedTable.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/16.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Table, Button } from 'antd';
|
||||
|
||||
const columns = [
|
||||
{ title: 'Name', dataIndex: 'name', key: 'name' },
|
||||
{ title: 'Age', dataIndex: 'age', key: 'age' },
|
||||
{ title: 'Address', dataIndex: 'address', key: 'address' },
|
||||
{ title: 'Action', dataIndex: '', key: 'x', render: () => <Button>Delete</Button> },
|
||||
];
|
||||
|
||||
const data = [
|
||||
{ key: 1, name: 'John Brown', age: 32, address: 'New York No. 1 Lake Park', description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.' },
|
||||
{ key: 2, name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park', description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' },
|
||||
{ key: 3, name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park', description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.' },
|
||||
];
|
||||
|
||||
const ExpandedTable = () => (
|
||||
<Table
|
||||
columns={columns}
|
||||
expandedRowRender={record => <p>{record.description}</p>}
|
||||
dataSource={data}
|
||||
/>
|
||||
);
|
||||
|
||||
export default ExpandedTable;
|
||||
45
src/components/tables/FixedTable.tsx
Normal file
45
src/components/tables/FixedTable.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/16.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Table } from 'antd';
|
||||
import { ColumnProps } from 'antd/lib/table';
|
||||
|
||||
const columns: ColumnProps<any>[] = [
|
||||
{ title: 'Full Name', width: 100, dataIndex: 'name', key: 'name', fixed: 'left' },
|
||||
{ title: 'Age', width: 100, dataIndex: 'age', key: 'age', fixed: 'left' },
|
||||
{ title: 'Column 1', dataIndex: 'address', key: '1' },
|
||||
{ title: 'Column 2', dataIndex: 'address', key: '2' },
|
||||
{ title: 'Column 3', dataIndex: 'address', key: '3' },
|
||||
{ title: 'Column 4', dataIndex: 'address', key: '4' },
|
||||
{ title: 'Column 5', dataIndex: 'address', key: '5' },
|
||||
{ title: 'Column 6', dataIndex: 'address', key: '6' },
|
||||
{ title: 'Column 7', dataIndex: 'address', key: '7' },
|
||||
{ title: 'Column 8', dataIndex: 'address', key: '8' },
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'operation',
|
||||
fixed: 'right',
|
||||
width: 100,
|
||||
render: () => <span>action</span>,
|
||||
},
|
||||
];
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York Park',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Jim Green',
|
||||
age: 40,
|
||||
address: 'London Park',
|
||||
},
|
||||
];
|
||||
|
||||
const FixedTable = () => <Table columns={columns} dataSource={data} scroll={{ x: 1300 }} />;
|
||||
|
||||
export default FixedTable;
|
||||
150
src/components/tables/SearchTable.tsx
Normal file
150
src/components/tables/SearchTable.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/16.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Table, Input, Button } from 'antd';
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Joe Black',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'Jim Green',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park',
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
name: 'Jim Red',
|
||||
age: 32,
|
||||
address: 'London No. 2 Lake Park',
|
||||
},
|
||||
];
|
||||
|
||||
class SearchTable extends React.Component {
|
||||
state = {
|
||||
filterDropdownVisible: false,
|
||||
data,
|
||||
searchText: '',
|
||||
filtered: false,
|
||||
};
|
||||
searchInput: any;
|
||||
onInputChange = (e: any) => {
|
||||
this.setState({ searchText: e.target.value });
|
||||
};
|
||||
onSearch = () => {
|
||||
const { searchText } = this.state;
|
||||
const reg = new RegExp(searchText, 'gi');
|
||||
this.setState({
|
||||
filterDropdownVisible: false,
|
||||
filtered: !!searchText,
|
||||
data: data
|
||||
.map((record) => {
|
||||
const match = record.name.match(reg);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...record,
|
||||
name: (
|
||||
<span>
|
||||
{record.name
|
||||
.split(reg)
|
||||
.map((text, i) =>
|
||||
i > 0
|
||||
? [<span className="highlight">{match[0]}</span>, text]
|
||||
: text
|
||||
)}
|
||||
</span>
|
||||
),
|
||||
};
|
||||
})
|
||||
.filter((record) => !!record),
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
filterDropdown: (
|
||||
<div className="custom-filter-dropdown">
|
||||
<Input
|
||||
ref={(ele) => (this.searchInput = ele)}
|
||||
placeholder="Search name"
|
||||
value={this.state.searchText}
|
||||
onChange={this.onInputChange}
|
||||
onPressEnter={this.onSearch}
|
||||
/>
|
||||
<Button type="primary" onClick={this.onSearch}>
|
||||
Search
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
filterIcon: (
|
||||
<SmileOutlined style={{ color: this.state.filtered ? '#108ee9' : '#aaa' }} />
|
||||
),
|
||||
filterDropdownVisible: this.state.filterDropdownVisible,
|
||||
onFilterDropdownVisibleChange: (visible: boolean) =>
|
||||
this.setState({ filterDropdownVisible: visible }, () =>
|
||||
this.searchInput.focus()
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
filters: [
|
||||
{
|
||||
text: 'London',
|
||||
value: 'London',
|
||||
},
|
||||
{
|
||||
text: 'New York',
|
||||
value: 'New York',
|
||||
},
|
||||
],
|
||||
onFilter: (value: any, record: any) => record.address.indexOf(value) === 0,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div>
|
||||
<Table columns={columns} dataSource={this.state.data} />
|
||||
<style>{`
|
||||
.custom-filter-dropdown {
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
|
||||
}
|
||||
.custom-filter-dropdown input {
|
||||
width: 130px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.highlight {
|
||||
color: #f50;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SearchTable;
|
||||
82
src/components/tables/SelectTable.tsx
Normal file
82
src/components/tables/SelectTable.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/15.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Table } from 'antd';
|
||||
// import { TableRowSelection } from 'antd/lib/table';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
},
|
||||
];
|
||||
|
||||
const data: any[] = [];
|
||||
for (let i = 0; i < 46; i++) {
|
||||
data.push({
|
||||
key: i,
|
||||
name: `Edward King ${i}`,
|
||||
age: 32,
|
||||
address: `London, Park Lane no. ${i}`,
|
||||
});
|
||||
}
|
||||
|
||||
class SelectTable extends React.Component {
|
||||
state = {
|
||||
selectedRowKeys: [], // Check here to configure the default column
|
||||
};
|
||||
onSelectChange = (selectedRowKeys: string[] | number[]) => {
|
||||
console.log('selectedRowKeys changed: ', selectedRowKeys);
|
||||
this.setState({ selectedRowKeys });
|
||||
};
|
||||
render() {
|
||||
const { selectedRowKeys } = this.state;
|
||||
const rowSelection: any = {
|
||||
selectedRowKeys,
|
||||
onChange: this.onSelectChange,
|
||||
selections: [
|
||||
{
|
||||
key: 'odd',
|
||||
text: '选择奇数列',
|
||||
onSelect: (changableRowKeys: string[]) => {
|
||||
let newSelectedRowKeys = [];
|
||||
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
|
||||
if (index % 2 !== 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
this.setState({ selectedRowKeys: newSelectedRowKeys });
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'even',
|
||||
text: '选择偶数列',
|
||||
onSelect: (changableRowKeys: string[]) => {
|
||||
let newSelectedRowKeys = [];
|
||||
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
|
||||
if (index % 2 !== 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
this.setState({ selectedRowKeys: newSelectedRowKeys });
|
||||
},
|
||||
},
|
||||
],
|
||||
// onSelection: this.onSelection,
|
||||
};
|
||||
return <Table rowSelection={rowSelection} columns={columns} dataSource={data} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default SelectTable;
|
||||
117
src/components/tables/SortTable.tsx
Normal file
117
src/components/tables/SortTable.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/15.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Table, Button } from 'antd';
|
||||
|
||||
const data = [
|
||||
{
|
||||
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',
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
name: 'Jim Red',
|
||||
age: 32,
|
||||
address: 'London No. 2 Lake Park',
|
||||
},
|
||||
];
|
||||
|
||||
type SortTableState = {
|
||||
filteredInfo: any;
|
||||
sortedInfo: any;
|
||||
};
|
||||
class SortTable extends React.Component<any, SortTableState> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
filteredInfo: {},
|
||||
sortedInfo: {},
|
||||
};
|
||||
}
|
||||
handleChange = (pagination: any, filters: any, sorter: any) => {
|
||||
console.log('Various parameters', pagination, filters, sorter);
|
||||
this.setState({
|
||||
filteredInfo: filters,
|
||||
sortedInfo: sorter,
|
||||
});
|
||||
};
|
||||
clearFilters = () => {
|
||||
this.setState({ filteredInfo: null });
|
||||
};
|
||||
clearAll = () => {
|
||||
this.setState({
|
||||
filteredInfo: null,
|
||||
sortedInfo: null,
|
||||
});
|
||||
};
|
||||
setAgeSort = () => {
|
||||
this.setState({
|
||||
sortedInfo: {
|
||||
order: 'descend',
|
||||
columnKey: 'age',
|
||||
},
|
||||
});
|
||||
};
|
||||
render() {
|
||||
let { sortedInfo, filteredInfo } = this.state;
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
filters: [{ text: 'Joe', value: 'Joe' }, { text: 'Jim', value: 'Jim' }],
|
||||
filteredValue: filteredInfo.name || null,
|
||||
onFilter: (value: any, record: any) => record.name.includes(value),
|
||||
sorter: (a: any, b: any) => a.name.length - b.name.length,
|
||||
sortOrder: sortedInfo.columnKey === 'name' && sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
sorter: (a: any, b: any) => a.age - b.age,
|
||||
sortOrder: sortedInfo.columnKey === 'age' && sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
filters: [
|
||||
{ text: 'London', value: 'London' },
|
||||
{ text: 'New York', value: 'New York' },
|
||||
],
|
||||
filteredValue: filteredInfo.address || null,
|
||||
onFilter: (value: any, record: any) => record.address.includes(value),
|
||||
sorter: (a: any, b: any) => a.address.length - b.address.length,
|
||||
sortOrder: sortedInfo.columnKey === 'address' && sortedInfo.order,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div>
|
||||
<div className="table-operations">
|
||||
<Button onClick={this.setAgeSort}>Sort age</Button>
|
||||
<Button onClick={this.clearFilters}>Clear filters</Button>
|
||||
<Button onClick={this.clearAll}>Clear filters and sorters</Button>
|
||||
</div>
|
||||
<Table columns={columns} dataSource={data} onChange={this.handleChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SortTable;
|
||||
167
src/components/ui/Buttons.tsx
Normal file
167
src/components/ui/Buttons.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/23.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card, Button, Radio, Menu, Dropdown } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { RadioChangeEvent } from 'antd/lib/radio';
|
||||
import { ButtonSize } from 'antd/lib/button';
|
||||
import { DownOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||
|
||||
type ButtonsState = {
|
||||
size: ButtonSize;
|
||||
loading: boolean;
|
||||
iconLoading: boolean;
|
||||
};
|
||||
class Buttons extends React.Component<any, ButtonsState> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
size: 'middle',
|
||||
loading: false,
|
||||
iconLoading: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleSizeChange = (e: RadioChangeEvent) => {
|
||||
this.setState({ size: e.target.value });
|
||||
};
|
||||
handleMenuClick = (e: any) => {
|
||||
console.log('click', e);
|
||||
};
|
||||
enterLoading = () => {
|
||||
this.setState({ loading: true });
|
||||
};
|
||||
enterIconLoading = () => {
|
||||
this.setState({ iconLoading: true });
|
||||
};
|
||||
render() {
|
||||
const size = this.state.size;
|
||||
const menu = (
|
||||
<Menu onClick={this.handleMenuClick}>
|
||||
<Menu.Item key="1">1st item</Menu.Item>
|
||||
<Menu.Item key="2">2nd item</Menu.Item>
|
||||
<Menu.Item key="3">3rd item</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom breads={['UI', '按钮']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Button type="primary">Primary</Button>
|
||||
<Button>Default</Button>
|
||||
<Button type="dashed">Dashed</Button>
|
||||
<Button danger>Danger</Button>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Button type="primary" shape="circle" icon="search" />
|
||||
<Button type="primary" icon="search">
|
||||
Search
|
||||
</Button>
|
||||
<Button shape="circle" icon="search" />
|
||||
<Button icon="search">Search</Button>
|
||||
<br />
|
||||
<Button shape="circle" icon="search" />
|
||||
<Button icon="search">Search</Button>
|
||||
<Button type="dashed" shape="circle" icon="search" />
|
||||
<Button type="dashed" icon="search">
|
||||
Search
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Radio.Group value={size} onChange={this.handleSizeChange}>
|
||||
<Radio.Button value="large">Large</Radio.Button>
|
||||
<Radio.Button value="middle">Middle</Radio.Button>
|
||||
<Radio.Button value="small">Small</Radio.Button>
|
||||
</Radio.Group>
|
||||
<br />
|
||||
<br />
|
||||
<Button type="primary" shape="circle" icon="download" size={size} />
|
||||
<Button type="primary" icon="download" size={size}>
|
||||
Download
|
||||
</Button>
|
||||
<Button type="primary" size={size}>
|
||||
Normal
|
||||
</Button>
|
||||
<br />
|
||||
<Button.Group size={size}>
|
||||
<Button type="primary">
|
||||
<LeftOutlined />
|
||||
Backward
|
||||
</Button>
|
||||
<Button type="primary">
|
||||
Forward
|
||||
<RightOutlined />
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Button type="primary">primary</Button>
|
||||
<Button>secondary</Button>
|
||||
<Dropdown overlay={menu}>
|
||||
<Button>
|
||||
more <DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Button type="primary" loading>
|
||||
Loading
|
||||
</Button>
|
||||
<Button type="primary" size="small" loading>
|
||||
Loading
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="primary"
|
||||
loading={this.state.loading}
|
||||
onClick={this.enterLoading}
|
||||
>
|
||||
Click me!
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon="poweroff"
|
||||
loading={this.state.iconLoading}
|
||||
onClick={this.enterIconLoading}
|
||||
>
|
||||
Click me!
|
||||
</Button>
|
||||
<br />
|
||||
<Button shape="circle" loading />
|
||||
<Button type="primary" shape="circle" loading />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<style>{`
|
||||
.button-demo .ant-btn {
|
||||
margin-right: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Buttons;
|
||||
132
src/components/ui/Draggable.tsx
Normal file
132
src/components/ui/Draggable.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/28.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import Draggable from 'react-draggable';
|
||||
|
||||
class Drags extends React.Component {
|
||||
state = {
|
||||
activeDrags: 0,
|
||||
deltaPosition: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
controlledPosition: {
|
||||
x: -400,
|
||||
y: 200,
|
||||
},
|
||||
};
|
||||
onStart = () => {
|
||||
let { activeDrags } = this.state;
|
||||
this.setState({ activeDrags: ++activeDrags });
|
||||
};
|
||||
onStop = () => {
|
||||
let { activeDrags } = this.state;
|
||||
this.setState({ activeDrags: --activeDrags });
|
||||
};
|
||||
handleDrag = (e: any, ui: any) => {
|
||||
const { x, y } = this.state.deltaPosition;
|
||||
this.setState({
|
||||
deltaPosition: {
|
||||
x: x + ui.deltaX,
|
||||
y: y + ui.deltaY,
|
||||
},
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const dragHandlers = { onStart: this.onStart, onStop: this.onStop };
|
||||
const { deltaPosition } = this.state;
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom breads={['UI', '拖拽']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={6}>
|
||||
<div className="gutter-box">
|
||||
<Draggable {...dragHandlers}>
|
||||
<Card bordered={false} className={'dragDemo'}>
|
||||
I can be dragged anywhere
|
||||
</Card>
|
||||
</Draggable>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={6}>
|
||||
<div className="gutter-box">
|
||||
<Draggable axis="x" {...dragHandlers}>
|
||||
<Card bordered={false} className={'dragDemo'}>
|
||||
I can only be dragged horizonally (x axis)
|
||||
</Card>
|
||||
</Draggable>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={6}>
|
||||
<div className="gutter-box">
|
||||
<Draggable axis="y" {...dragHandlers}>
|
||||
<Card bordered={false} className={'dragDemo'}>
|
||||
I can only be dragged vertically (y axis)
|
||||
</Card>
|
||||
</Draggable>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={6}>
|
||||
<div className="gutter-box">
|
||||
<Draggable onDrag={this.handleDrag} {...dragHandlers}>
|
||||
<Card bordered={false} className={'dragDemo'}>
|
||||
<div>I track my deltas</div>
|
||||
<div>
|
||||
x: {deltaPosition.x.toFixed(0)}, y:{' '}
|
||||
{deltaPosition.y.toFixed(0)}
|
||||
</div>
|
||||
</Card>
|
||||
</Draggable>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={6}>
|
||||
<div className="gutter-box">
|
||||
<Draggable handle="strong" {...dragHandlers}>
|
||||
<Card bordered={false} className={'dragDemo no-cursor'}>
|
||||
<strong className="cursor-move">
|
||||
<div>Drag here</div>
|
||||
</strong>
|
||||
<div>You must click my handle to drag me</div>
|
||||
</Card>
|
||||
</Draggable>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={6}>
|
||||
<div className="gutter-box">
|
||||
<Draggable cancel="strong" {...dragHandlers}>
|
||||
<Card bordered={false} className={'dragDemo'}>
|
||||
<strong className="no-cursor">
|
||||
<div>Can't drag here</div>
|
||||
</strong>
|
||||
<div>Dragging here works</div>
|
||||
</Card>
|
||||
</Draggable>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={6}>
|
||||
<div className="gutter-box">
|
||||
<Draggable
|
||||
bounds={{ top: -100, left: -100, right: 100, bottom: 100 }}
|
||||
{...dragHandlers}
|
||||
>
|
||||
<Card bordered={false} className={'dragDemo'}>
|
||||
<div>I can only be moved 100px in any direction.</div>
|
||||
</Card>
|
||||
</Draggable>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<style>{`
|
||||
.dragDemo {
|
||||
height: 180px;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Drags;
|
||||
223
src/components/ui/Gallery.tsx
Normal file
223
src/components/ui/Gallery.tsx
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/5/6.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import PhotoSwipe from 'photoswipe';
|
||||
import PhotoswipeUIDefault from 'photoswipe/dist/photoswipe-ui-default';
|
||||
import 'photoswipe/dist/photoswipe.css';
|
||||
import 'photoswipe/dist/default-skin/default-skin.css';
|
||||
|
||||
class Gallery extends React.Component {
|
||||
state = {
|
||||
gallery: null,
|
||||
};
|
||||
componentDidMount() {}
|
||||
componentWillUnmount = () => {
|
||||
this.closeGallery();
|
||||
};
|
||||
pswpElement: any;
|
||||
gallery: any;
|
||||
openGallery = (item: any) => {
|
||||
const items = [
|
||||
{
|
||||
src: item,
|
||||
w: 0,
|
||||
h: 0,
|
||||
},
|
||||
];
|
||||
const pswpElement = this.pswpElement;
|
||||
const options = { index: 0 };
|
||||
this.gallery = new PhotoSwipe(pswpElement, PhotoswipeUIDefault, items, options);
|
||||
this.gallery.listen('gettingData', (index: number, item: any) => {
|
||||
const _this = this;
|
||||
if (item.w < 1 || item.h < 1) {
|
||||
// unknown size
|
||||
var img = new Image();
|
||||
img.onload = function () {
|
||||
// will get size after load
|
||||
item.w = (this as any).width; // set image width
|
||||
item.h = (this as any).height; // set image height
|
||||
_this.gallery.invalidateCurrItems(); // reinit Items
|
||||
_this.gallery.updateSize(true); // reinit Items
|
||||
};
|
||||
img.src = item.src; // let's download image
|
||||
}
|
||||
});
|
||||
this.gallery.init();
|
||||
};
|
||||
closeGallery = () => {
|
||||
if (!this.gallery) return;
|
||||
this.gallery.close();
|
||||
};
|
||||
render() {
|
||||
const imgs = [
|
||||
[
|
||||
'http://img.hb.aicdn.com/1cad414972c5db2b8c1942289e3aeef37175006a8bb16-CBtjtX_fw',
|
||||
'http://img.hb.aicdn.com/016f2e13934397e17c3482a4529f3da1149d37fd2a99c-RVM1Gi_fw',
|
||||
'http://img.hb.aicdn.com/8c5d5f2bf6427d1b5ed8657a7ae0c9938d3465e367899-AJ0zVA_fw',
|
||||
'http://img.hb.aicdn.com/bd71ccac0b16bbcade255a1a8a63504d71c7dee9a8652-zBCN9d_fw',
|
||||
'http://img.hb.aicdn.com/37a40cb04345463858d45418ae6ed9ef319e30dc37a45-o4pQ0j_fw',
|
||||
],
|
||||
[
|
||||
'http://img.hb.aicdn.com/5fad6c3a14a9b80c4448835bb6b23ab895d18e234eff3-BPGmox_fw',
|
||||
'http://img.hb.aicdn.com/a1a19de5dac212a646ba6967ef565786399fb1665bd04-EEvwzR_fw',
|
||||
'http://img.hb.aicdn.com/06595f8044e881de3a82d691768bc8c21a2a9f3633d60-XKjC2s_fw',
|
||||
'http://img.hb.aicdn.com/880787b36d45efbe05aa409c867db29a3028e02da7f9b-qxGib9_fw',
|
||||
'http://img.hb.aicdn.com/4964b97f6f6eb61a20922b40842adf0169c44e491c4b60-azX1S7_fw',
|
||||
],
|
||||
[
|
||||
'http://img.hb.aicdn.com/ff97d00944edfc706c62dd5c0e955c4099a37b407534f-BcUqf0_fw',
|
||||
'http://img.hb.aicdn.com/0e22be22b08c6f78b94283b6cfa890093ac3cae8401e7-b1ftfi_fw',
|
||||
'http://img.hb.aicdn.com/879f870e15f7cc0847c8ae19a5fcbe974d5904bb181d7-RGmtNU_fw',
|
||||
'http://img.hb.aicdn.com/b4a8e62958555a97dc3de9ccb03284bf556c042925522-x50qGv_fw',
|
||||
'http://img.hb.aicdn.com/1ef493a15674e9fd523b248ea4ec43d2ea9ce6952ff3e-WavWKc_fw',
|
||||
],
|
||||
[
|
||||
'http://img.hb.aicdn.com/8e16efec78ac4a3684fc8999d18e3661af40fd4510a25-DDvQON_fw',
|
||||
'http://img.hb.aicdn.com/61dfa024c8040e6a5bcb03d42928fbcb0c87c1a54e731-yc4lvV_fw',
|
||||
'http://img.hb.aicdn.com/6783b4d7811ad7fb87b1446c5488b91179f7608118289-hpEyP3_fw',
|
||||
'http://img.hb.aicdn.com/7be61ba6bdb20a73be63edc387b16eec72d0bbb51c7ef-XafA07_fw',
|
||||
'http://img.hb.aicdn.com/bd3ba3f907fe098b911947e0020615b50fc340ed2df72-WsuHuM_fw',
|
||||
],
|
||||
[
|
||||
'http://img.hb.aicdn.com/71471aaac95eade66400a390863b37c76d9addcd14982-0H6sak_fw',
|
||||
'http://img.hb.aicdn.com/cb16c68c4d3b7a08b5e91cd351f6b723634ca3fc27d4d-m1JD8z_fw',
|
||||
'http://img.hb.aicdn.com/e3559b6e8d7237857382050e5659a64cc0b7d696a2869-stcRXA_fw',
|
||||
'http://img.hb.aicdn.com/4ea229436fcf2077502953907a6afb16d3c5cd611b8e2-0dVIeH_fw',
|
||||
'http://img.hb.aicdn.com/98c786f4314736f95a42bf927bf65a82d305a532c6258-njI6id_fw',
|
||||
],
|
||||
];
|
||||
const imgsTag = imgs.map((v1) =>
|
||||
v1.map((v2) => (
|
||||
<div className="gutter-box" key={v2}>
|
||||
<Card bordered={false} bodyStyle={{ padding: 0 }}>
|
||||
<div>
|
||||
<img
|
||||
onClick={() => this.openGallery(v2)}
|
||||
alt="example"
|
||||
width="100%"
|
||||
src={v2}
|
||||
/>
|
||||
</div>
|
||||
<div className="pa-m">
|
||||
<h3>React Admin</h3>
|
||||
<small>
|
||||
<a
|
||||
href="https://yezihaohao.github.io/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
https://yezihaohao.github.io/
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
))
|
||||
);
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom
|
||||
breads={['UI', '画廊(图片来自花瓣网,仅学习,若侵权请联系删除)']}
|
||||
/>
|
||||
<Row gutter={10}>
|
||||
<Col className="gutter-row" md={5}>
|
||||
{imgsTag[0]}
|
||||
</Col>
|
||||
<Col className="gutter-row" md={5}>
|
||||
{imgsTag[1]}
|
||||
</Col>
|
||||
<Col className="gutter-row" md={5}>
|
||||
{imgsTag[2]}
|
||||
</Col>
|
||||
<Col className="gutter-row" md={5}>
|
||||
{imgsTag[3]}
|
||||
</Col>
|
||||
<Col className="gutter-row" md={4}>
|
||||
{imgsTag[4]}
|
||||
</Col>
|
||||
</Row>
|
||||
<div
|
||||
className="pswp"
|
||||
tabIndex={-1}
|
||||
role="dialog"
|
||||
aria-hidden="true"
|
||||
ref={(div) => {
|
||||
this.pswpElement = div;
|
||||
}}
|
||||
>
|
||||
<div className="pswp__bg" />
|
||||
|
||||
<div className="pswp__scroll-wrap">
|
||||
<div className="pswp__container">
|
||||
<div className="pswp__item" />
|
||||
<div className="pswp__item" />
|
||||
<div className="pswp__item" />
|
||||
</div>
|
||||
|
||||
<div className="pswp__ui pswp__ui--hidden">
|
||||
<div className="pswp__top-bar">
|
||||
<div className="pswp__counter" />
|
||||
|
||||
<button
|
||||
className="pswp__button pswp__button--close"
|
||||
title="Close (Esc)"
|
||||
/>
|
||||
|
||||
<button
|
||||
className="pswp__button pswp__button--share"
|
||||
title="Share"
|
||||
/>
|
||||
|
||||
<button
|
||||
className="pswp__button pswp__button--fs"
|
||||
title="Toggle fullscreen"
|
||||
/>
|
||||
|
||||
<button
|
||||
className="pswp__button pswp__button--zoom"
|
||||
title="Zoom in/out"
|
||||
/>
|
||||
|
||||
<div className="pswp__preloader">
|
||||
<div className="pswp__preloader__icn">
|
||||
<div className="pswp__preloader__cut">
|
||||
<div className="pswp__preloader__donut" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
|
||||
<div className="pswp__share-tooltip" />
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="pswp__button pswp__button--arrow--left"
|
||||
title="Previous (arrow left)"
|
||||
/>
|
||||
|
||||
<button
|
||||
className="pswp__button pswp__button--arrow--right"
|
||||
title="Next (arrow right)"
|
||||
/>
|
||||
|
||||
<div className="pswp__caption">
|
||||
<div className="pswp__caption__center" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>{`
|
||||
.ant-card-body img {
|
||||
cursor: pointer;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Gallery;
|
||||
309
src/components/ui/Icons.tsx
Normal file
309
src/components/ui/Icons.tsx
Normal file
@@ -0,0 +1,309 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/22.
|
||||
*/
|
||||
import React from 'react';
|
||||
import Emoji from './emoji';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
|
||||
const emojiList = () => {
|
||||
let _elements = [];
|
||||
for (let i = 1; i < 30; i++) {
|
||||
_elements.push(
|
||||
<li key={i}>
|
||||
<Emoji type={'emoji-' + i} />
|
||||
<span>{'emoji-' + i}</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
return _elements;
|
||||
};
|
||||
|
||||
const icons = {
|
||||
direction: [
|
||||
'step-backward',
|
||||
'step-forward',
|
||||
'fast-backward',
|
||||
'fast-forward',
|
||||
'shrink',
|
||||
'arrows-alt',
|
||||
'down',
|
||||
'up',
|
||||
'left',
|
||||
'right',
|
||||
'caret-up',
|
||||
'caret-down',
|
||||
'caret-left',
|
||||
'caret-right',
|
||||
'up-circle',
|
||||
'down-circle',
|
||||
'left-circle',
|
||||
'right-circle',
|
||||
'up-circle-o',
|
||||
'down-circle-o',
|
||||
'right-circle-o',
|
||||
'left-circle-o',
|
||||
'double-right',
|
||||
'double-left',
|
||||
'verticle-left',
|
||||
'verticle-right',
|
||||
'forward',
|
||||
'backward',
|
||||
'rollback',
|
||||
'enter',
|
||||
'retweet',
|
||||
'swap',
|
||||
'swap-left',
|
||||
'swap-right',
|
||||
'arrow-up',
|
||||
'arrow-down',
|
||||
'arrow-left',
|
||||
'arrow-right',
|
||||
'play-circle',
|
||||
'play-circle-o',
|
||||
'up-square',
|
||||
'down-square',
|
||||
'left-square',
|
||||
'right-square',
|
||||
'up-square-o',
|
||||
'down-square-o',
|
||||
'left-square-o',
|
||||
'right-square-o',
|
||||
'login',
|
||||
'logout',
|
||||
'menu-fold',
|
||||
'menu-unfold',
|
||||
],
|
||||
suggestion: [
|
||||
'question',
|
||||
'question-circle-o',
|
||||
'question-circle',
|
||||
'plus',
|
||||
'plus-circle-o',
|
||||
'plus-circle',
|
||||
'pause',
|
||||
'pause-circle-o',
|
||||
'pause-circle',
|
||||
'minus',
|
||||
'minus-circle-o',
|
||||
'minus-circle',
|
||||
'plus-square',
|
||||
'plus-square-o',
|
||||
'minus-square',
|
||||
'minus-square-o',
|
||||
'info',
|
||||
'info-circle-o',
|
||||
'info-circle',
|
||||
'exclamation',
|
||||
'exclamation-circle-o',
|
||||
'exclamation-circle',
|
||||
'close',
|
||||
'close-circle',
|
||||
'close-circle-o',
|
||||
'close-square',
|
||||
'close-square-o',
|
||||
'check',
|
||||
'check-circle',
|
||||
'check-circle-o',
|
||||
'check-square',
|
||||
'check-square-o',
|
||||
'clock-circle-o',
|
||||
'clock-circle',
|
||||
],
|
||||
logo: [
|
||||
'android',
|
||||
'android-o',
|
||||
'apple',
|
||||
'apple-o',
|
||||
'windows',
|
||||
'windows-o',
|
||||
'ie',
|
||||
'chrome',
|
||||
'github',
|
||||
'aliwangwang',
|
||||
'aliwangwang-o',
|
||||
'dingding',
|
||||
'dingding-o',
|
||||
],
|
||||
other: [
|
||||
'lock',
|
||||
'unlock',
|
||||
'area-chart',
|
||||
'pie-chart',
|
||||
'bar-chart',
|
||||
'dot-chart',
|
||||
'bars',
|
||||
'book',
|
||||
'calendar',
|
||||
'cloud',
|
||||
'cloud-download',
|
||||
'code',
|
||||
'code-o',
|
||||
'copy',
|
||||
'credit-card',
|
||||
'delete',
|
||||
'desktop',
|
||||
'download',
|
||||
'edit',
|
||||
'ellipsis',
|
||||
'file',
|
||||
'file-text',
|
||||
'file-unknown',
|
||||
'file-pdf',
|
||||
'file-excel',
|
||||
'file-jpg',
|
||||
'file-ppt',
|
||||
'file-add',
|
||||
'folder',
|
||||
'folder-open',
|
||||
'folder-add',
|
||||
'hdd',
|
||||
'frown',
|
||||
'frown-o',
|
||||
'meh',
|
||||
'meh-o',
|
||||
'smile',
|
||||
'smile-o',
|
||||
'inbox',
|
||||
'laptop',
|
||||
'appstore-o',
|
||||
'appstore',
|
||||
'line-chart',
|
||||
'link',
|
||||
'mail',
|
||||
'mobile',
|
||||
'notification',
|
||||
'paper-clip',
|
||||
'picture',
|
||||
'poweroff',
|
||||
'reload',
|
||||
'search',
|
||||
'setting',
|
||||
'share-alt',
|
||||
'shopping-cart',
|
||||
'tablet',
|
||||
'tag',
|
||||
'tag-o',
|
||||
'tags',
|
||||
'tags-o',
|
||||
'to-top',
|
||||
'upload',
|
||||
'user',
|
||||
'video-camera',
|
||||
'home',
|
||||
'loading',
|
||||
'loading-3-quarters',
|
||||
'cloud-upload-o',
|
||||
'cloud-download-o',
|
||||
'cloud-upload',
|
||||
'cloud-o',
|
||||
'star-o',
|
||||
'star',
|
||||
'heart-o',
|
||||
'heart',
|
||||
'environment',
|
||||
'environment-o',
|
||||
'eye',
|
||||
'eye-o',
|
||||
'camera',
|
||||
'camera-o',
|
||||
'save',
|
||||
'team',
|
||||
'solution',
|
||||
'phone',
|
||||
'filter',
|
||||
'exception',
|
||||
'export',
|
||||
'customer-service',
|
||||
'qrcode',
|
||||
'scan',
|
||||
'like',
|
||||
'like-o',
|
||||
'dislike',
|
||||
'dislike-o',
|
||||
'message',
|
||||
'pay-circle',
|
||||
'pay-circle-o',
|
||||
'calculator',
|
||||
'pushpin',
|
||||
'pushpin-o',
|
||||
'bulb',
|
||||
'select',
|
||||
'switcher',
|
||||
'rocket',
|
||||
'bell',
|
||||
'disconnect',
|
||||
'database',
|
||||
'compass',
|
||||
'barcode',
|
||||
'hourglass',
|
||||
'key',
|
||||
'flag',
|
||||
'layout',
|
||||
'printer',
|
||||
'sound',
|
||||
'usb',
|
||||
'skin',
|
||||
'tool',
|
||||
'sync',
|
||||
'wifi',
|
||||
'car',
|
||||
'schedule',
|
||||
'user-add',
|
||||
'user-delete',
|
||||
'usergroup-add',
|
||||
'usergroup-delete',
|
||||
'man',
|
||||
'woman',
|
||||
'shop',
|
||||
'gift',
|
||||
'idcard',
|
||||
'medicine-box',
|
||||
'red-envelope',
|
||||
'coffee',
|
||||
'copyright',
|
||||
'trademark',
|
||||
'safety',
|
||||
'wallet',
|
||||
'bank',
|
||||
'trophy',
|
||||
'contacts',
|
||||
'global',
|
||||
'shake',
|
||||
'api',
|
||||
],
|
||||
};
|
||||
const iconsList = Object.keys(icons).map((v) =>
|
||||
(icons as any)[v].map((icon: any, i: number) => (
|
||||
<li key={i}>
|
||||
{/* <Icon type={icon} style={{ fontSize: 15 }} /> */}
|
||||
<span>{icon}</span>
|
||||
</li>
|
||||
))
|
||||
);
|
||||
|
||||
const Icons = () => (
|
||||
<div className="gutter-example">
|
||||
<BreadcrumbCustom breads={['UI', '图标']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<ul className="icons-list">{emojiList()}</ul>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<ul className="icons-list">{iconsList}</ul>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Icons;
|
||||
250
src/components/ui/Modals.tsx
Normal file
250
src/components/ui/Modals.tsx
Normal file
@@ -0,0 +1,250 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/23.
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import { Row, Col, Card, Modal, Button } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
const confirm = Modal.confirm;
|
||||
|
||||
class S extends Component {
|
||||
state = {
|
||||
visible: false,
|
||||
ModalText2: 'Content of the modal dialog',
|
||||
visible2: false,
|
||||
loading3: false,
|
||||
visible3: false,
|
||||
modal1Visible: false,
|
||||
modal2Visible: false,
|
||||
confirmLoading2: false,
|
||||
};
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
handleOk = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
console.log(e);
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
handleCancel = (e: React.MouseEvent<HTMLElement>) => {
|
||||
console.log(e);
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
showModal2 = () => {
|
||||
this.setState({
|
||||
visible2: true,
|
||||
});
|
||||
};
|
||||
handleOk2 = () => {
|
||||
this.setState({
|
||||
ModalText2: 'The modal dialog will be closed after two seconds',
|
||||
confirmLoading2: true,
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
visible2: false,
|
||||
confirmLoading2: false,
|
||||
});
|
||||
}, 2000);
|
||||
};
|
||||
setModal1Visible = (modal1Visible: boolean) => {
|
||||
this.setState({ modal1Visible });
|
||||
};
|
||||
setModal2Visible = (modal2Visible: boolean) => {
|
||||
this.setState({ modal2Visible });
|
||||
};
|
||||
handleCancel2 = () => {
|
||||
console.log('Clicked cancel button');
|
||||
this.setState({
|
||||
visible2: false,
|
||||
});
|
||||
};
|
||||
showModal3 = () => {
|
||||
this.setState({
|
||||
visible3: true,
|
||||
});
|
||||
};
|
||||
handleOk3 = () => {
|
||||
this.setState({ loading3: true });
|
||||
setTimeout(() => {
|
||||
this.setState({ loading3: false, visible3: false });
|
||||
}, 3000);
|
||||
};
|
||||
handleCancel3 = () => {
|
||||
this.setState({ visible3: false });
|
||||
};
|
||||
showConfirm4 = () => {
|
||||
confirm({
|
||||
title: 'Want to delete these items?',
|
||||
content: 'some descriptions',
|
||||
onOk() {
|
||||
console.log('OK');
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
},
|
||||
});
|
||||
};
|
||||
info = () => {
|
||||
Modal.info({
|
||||
title: 'This is a notification message',
|
||||
content: (
|
||||
<div>
|
||||
<p>some messages...some messages...</p>
|
||||
<p>some messages...some messages...</p>
|
||||
</div>
|
||||
),
|
||||
onOk() {},
|
||||
});
|
||||
};
|
||||
success = () => {
|
||||
Modal.success({
|
||||
title: 'This is a success message',
|
||||
content: 'some messages...some messages...',
|
||||
});
|
||||
};
|
||||
error = () => {
|
||||
Modal.error({
|
||||
title: 'This is an error message',
|
||||
content: 'some messages...some messages...',
|
||||
});
|
||||
};
|
||||
warning = () => {
|
||||
Modal.warning({
|
||||
title: 'This is a warning message',
|
||||
content: 'some messages...some messages...',
|
||||
});
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom breads={['UI', '对话框']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<p>
|
||||
<Button type="primary" onClick={this.showModal}>
|
||||
基本用法
|
||||
</Button>
|
||||
<Modal
|
||||
title="Basic Modal"
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
>
|
||||
<p>some contents...</p>
|
||||
<p>some contents...</p>
|
||||
<p>some contents...</p>
|
||||
</Modal>
|
||||
</p>
|
||||
<p>
|
||||
<Button type="primary" onClick={this.showModal2}>
|
||||
异步关闭
|
||||
</Button>
|
||||
<Modal
|
||||
title="Title of the modal dialog"
|
||||
visible={this.state.visible2}
|
||||
onOk={this.handleOk2}
|
||||
confirmLoading={this.state.confirmLoading2}
|
||||
onCancel={this.handleCancel2}
|
||||
>
|
||||
<p>{this.state.ModalText2}</p>
|
||||
</Modal>
|
||||
</p>
|
||||
<p>
|
||||
<Button type="primary" onClick={this.showModal3}>
|
||||
自定义页脚
|
||||
</Button>
|
||||
<Modal
|
||||
visible={this.state.visible3}
|
||||
title="Title"
|
||||
onOk={this.handleOk3}
|
||||
onCancel={this.handleCancel3}
|
||||
footer={[
|
||||
<Button
|
||||
key="back"
|
||||
size="large"
|
||||
onClick={this.handleCancel3}
|
||||
>
|
||||
Return
|
||||
</Button>,
|
||||
<Button
|
||||
key="submit"
|
||||
type="primary"
|
||||
size="large"
|
||||
loading={this.state.loading3}
|
||||
onClick={this.handleOk3}
|
||||
>
|
||||
Submit
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
</Modal>
|
||||
</p>
|
||||
<p>
|
||||
<Button onClick={this.showConfirm4}>确认框</Button>
|
||||
</p>
|
||||
<p>
|
||||
<Button onClick={this.info}>信息提示</Button>
|
||||
<Button onClick={this.success}>成功</Button>
|
||||
<Button onClick={this.error}>失败</Button>
|
||||
<Button onClick={this.warning}>警告</Button>
|
||||
</p>
|
||||
<p>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.setModal1Visible(true)}
|
||||
>
|
||||
距离顶部20px
|
||||
</Button>
|
||||
<Modal
|
||||
title="20px to Top"
|
||||
style={{ top: 20 }}
|
||||
visible={this.state.modal1Visible}
|
||||
onOk={() => this.setModal1Visible(false)}
|
||||
onCancel={() => this.setModal1Visible(false)}
|
||||
>
|
||||
<p>some contents...</p>
|
||||
<p>some contents...</p>
|
||||
<p>some contents...</p>
|
||||
</Modal>
|
||||
<br />
|
||||
<br />
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.setModal2Visible(true)}
|
||||
>
|
||||
垂直居中
|
||||
</Button>
|
||||
<Modal
|
||||
title="Vertically centered modal dialog"
|
||||
wrapClassName="vertical-center-modal"
|
||||
visible={this.state.modal2Visible}
|
||||
onOk={() => this.setModal2Visible(false)}
|
||||
onCancel={() => this.setModal2Visible(false)}
|
||||
>
|
||||
<p>some contents...</p>
|
||||
<p>some contents...</p>
|
||||
<p>some contents...</p>
|
||||
</Modal>
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default S;
|
||||
166
src/components/ui/Notifications.tsx
Normal file
166
src/components/ui/Notifications.tsx
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/25.
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import { Row, Col, Card, Button, notification, Select } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { IconType, ConfigProps } from 'antd/lib/notification';
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
const { Option } = Select;
|
||||
const options = ['topLeft', 'topRight', 'bottomLeft', 'bottomRight'];
|
||||
class Notifications extends Component {
|
||||
openNotification = () => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
|
||||
});
|
||||
};
|
||||
openNotification2 = () => {
|
||||
const args = {
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'I will never close automatically. I will be close automatically. I will never close automatically.',
|
||||
duration: 0,
|
||||
};
|
||||
notification.open(args);
|
||||
};
|
||||
openNotificationWithIcon = (type: IconType) => {
|
||||
notification[type]({
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
|
||||
});
|
||||
};
|
||||
openNotification3 = () => {
|
||||
const key = `open${Date.now()}`;
|
||||
const btnClick = function () {
|
||||
// to hide notification box
|
||||
notification.close(key);
|
||||
};
|
||||
const btn = (
|
||||
<Button type="primary" size="small" onClick={btnClick}>
|
||||
Confirm
|
||||
</Button>
|
||||
);
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'A function will be be called after the notification is closed (automatically after the "duration" time of manually).',
|
||||
btn,
|
||||
key,
|
||||
onClose: this.close,
|
||||
});
|
||||
};
|
||||
close = () => {
|
||||
console.log(
|
||||
'Notification was closed. Either the close button was clicked or duration time elapsed.'
|
||||
);
|
||||
};
|
||||
openNotification4 = () => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
description:
|
||||
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
|
||||
icon: <SmileOutlined style={{ color: '#108ee9' }} />,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom breads={['UI', '通知提醒框']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Button type="primary" onClick={this.openNotification}>
|
||||
基本用法-4.5秒关闭
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Button type="primary" onClick={this.openNotification2}>
|
||||
取消自动关闭
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.openNotificationWithIcon('success')}
|
||||
>
|
||||
成功
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.openNotificationWithIcon('info')}
|
||||
>
|
||||
提醒
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.openNotificationWithIcon('warning')}
|
||||
>
|
||||
警告
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.openNotificationWithIcon('error')}
|
||||
>
|
||||
失败
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Button type="primary" onClick={this.openNotification3}>
|
||||
自定义按钮
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Button type="primary" onClick={this.openNotification4}>
|
||||
自定义通知图标
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Select
|
||||
defaultValue="topRight"
|
||||
style={{ width: 120, marginRight: 10 }}
|
||||
onChange={(val: ConfigProps['placement']) => {
|
||||
notification.config({
|
||||
placement: val,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{options.map((val) => (
|
||||
<Option key={val} value={val}>
|
||||
{val}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
<Button type="primary" onClick={this.openNotification}>
|
||||
打开消息通知
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Notifications;
|
||||
95
src/components/ui/Spins.tsx
Normal file
95
src/components/ui/Spins.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/23.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card, Spin, Alert, Switch, Button } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
class Spins extends React.Component {
|
||||
state = { loading: false };
|
||||
toggle = (value: boolean) => {
|
||||
this.setState({ loading: value });
|
||||
};
|
||||
nprogressStart = () => {
|
||||
NProgress.start();
|
||||
};
|
||||
nprogressDone = () => {
|
||||
NProgress.done();
|
||||
};
|
||||
render() {
|
||||
const container = (
|
||||
<Alert
|
||||
message="Alert message title"
|
||||
description="Further details about the context of this alert."
|
||||
type="info"
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom breads={['UI', '加载中']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Spin />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card>
|
||||
<Spin size="small" />
|
||||
<Spin />
|
||||
<Spin size="large" />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Spin tip="Loading...">
|
||||
<Alert
|
||||
message="Alert message title"
|
||||
description="Further details about the context of this alert."
|
||||
type="info"
|
||||
/>
|
||||
</Spin>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<Spin spinning={this.state.loading}>{container}</Spin>
|
||||
Loading state:
|
||||
<Switch checked={this.state.loading} onChange={this.toggle} />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card bordered={false}>
|
||||
<h4>顶部进度条</h4>
|
||||
<p>
|
||||
<Button icon="caret-right" onClick={this.nprogressStart} />
|
||||
<span> NProgress.start() — 显示进度条</span>
|
||||
</p>
|
||||
<p style={{ marginTop: 20 }}>
|
||||
<Button icon="caret-right" onClick={this.nprogressDone} />
|
||||
<span> NProgress.done() — 进度条完成</span>
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Spins;
|
||||
218
src/components/ui/Tabs.tsx
Normal file
218
src/components/ui/Tabs.tsx
Normal file
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/25.
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import { Row, Col, Card, Tabs, Radio, Button } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { RadioChangeEvent } from 'antd/lib/radio';
|
||||
import { TabsPosition } from 'antd/lib/tabs';
|
||||
import { AndroidOutlined, AppleOutlined } from '@ant-design/icons';
|
||||
const TabPane = Tabs.TabPane;
|
||||
|
||||
type TabsCustomState = {
|
||||
activeKey: string;
|
||||
panes: any;
|
||||
mode: TabsPosition;
|
||||
};
|
||||
class TabsCustom extends Component<any, TabsCustomState> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
const panes = [
|
||||
{ title: 'Tab 1', content: 'Content of Tab Pane 1', key: '1' },
|
||||
{ title: 'Tab 2', content: 'Content of Tab Pane 2', key: '2' },
|
||||
];
|
||||
this.state = {
|
||||
activeKey: panes[0].key,
|
||||
panes,
|
||||
mode: 'top',
|
||||
};
|
||||
}
|
||||
newTabIndex: number = 0;
|
||||
callback = (key: string) => {
|
||||
console.log(key);
|
||||
};
|
||||
handleModeChange = (e: RadioChangeEvent) => {
|
||||
const mode = e.target.value;
|
||||
this.setState({ mode });
|
||||
};
|
||||
onChange = (activeKey: string) => {
|
||||
this.setState({ activeKey });
|
||||
};
|
||||
onEdit = (targetKey: string | React.MouseEvent<HTMLElement>, action: 'add' | 'remove'): any => {
|
||||
this[action](targetKey as string);
|
||||
};
|
||||
add = () => {
|
||||
const panes = this.state.panes;
|
||||
const activeKey = `newTab${this.newTabIndex++}`;
|
||||
panes.push({ title: 'New Tab', content: 'New Tab Pane', key: activeKey });
|
||||
this.setState({ panes, activeKey });
|
||||
};
|
||||
remove = (targetKey: string) => {
|
||||
let activeKey = this.state.activeKey;
|
||||
let lastIndex = 0;
|
||||
this.state.panes.forEach((pane: any, i: number) => {
|
||||
if (pane.key === targetKey) {
|
||||
lastIndex = i - 1;
|
||||
}
|
||||
});
|
||||
const panes = this.state.panes.filter((pane: any) => pane.key !== targetKey);
|
||||
if (lastIndex >= 0 && activeKey === targetKey) {
|
||||
activeKey = panes[lastIndex].key;
|
||||
}
|
||||
this.setState({ panes, activeKey });
|
||||
};
|
||||
render() {
|
||||
const { mode } = this.state;
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom breads={['UI', '标签页']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card title="基本-默认选中第一项" bordered={false}>
|
||||
<Tabs defaultActiveKey="1" onChange={this.callback}>
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Content of Tab Pane 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of Tab Pane 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of Tab Pane 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="gutter-box">
|
||||
<Card title="带图标" bordered={false}>
|
||||
<Tabs defaultActiveKey="2" style={{ height: 150 }}>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<AppleOutlined />
|
||||
Tab 1
|
||||
</span>
|
||||
}
|
||||
key="1"
|
||||
>
|
||||
Tab 1
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<AndroidOutlined />
|
||||
Tab 2
|
||||
</span>
|
||||
}
|
||||
key="2"
|
||||
>
|
||||
Tab 2
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="gutter-box">
|
||||
<Card title="卡片式风格" bordered={false}>
|
||||
<Tabs onChange={this.callback} type="card">
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Content of Tab Pane 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of Tab Pane 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of Tab Pane 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={12}>
|
||||
<div className="gutter-box">
|
||||
<Card title="禁用某项" bordered={false}>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Tab 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" disabled key="2">
|
||||
Tab 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Tab 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="gutter-box">
|
||||
<Card title="带滚动" bordered={false}>
|
||||
<Radio.Group
|
||||
onChange={this.handleModeChange}
|
||||
value={mode}
|
||||
style={{ marginBottom: 8 }}
|
||||
>
|
||||
<Radio.Button value="top">Horizontal</Radio.Button>
|
||||
<Radio.Button value="left">Vertical</Radio.Button>
|
||||
</Radio.Group>
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
tabPosition={mode}
|
||||
style={{ height: 150 }}
|
||||
>
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Content of tab 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of tab 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of tab 3
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 4" key="4">
|
||||
Content of tab 4
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 5" key="5">
|
||||
Content of tab 5
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 6" key="6">
|
||||
Content of tab 6
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 7" key="7">
|
||||
Content of tab 7
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 8" key="8">
|
||||
Content of tab 8
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 9" key="9">
|
||||
Content of tab 9
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="gutter-box">
|
||||
<Card title="带删除和新增" bordered={false}>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Button onClick={this.add}>ADD</Button>
|
||||
</div>
|
||||
<Tabs
|
||||
hideAdd
|
||||
onChange={this.onChange}
|
||||
activeKey={this.state.activeKey}
|
||||
type="editable-card"
|
||||
// onEdit={this.onEdit}
|
||||
>
|
||||
{this.state.panes.map((pane: any) => (
|
||||
<TabPane tab={pane.title} key={pane.key}>
|
||||
{pane.content}
|
||||
</TabPane>
|
||||
))}
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TabsCustom;
|
||||
195
src/components/ui/Wysiwyg.tsx
Normal file
195
src/components/ui/Wysiwyg.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/26.
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import BreadcrumbCustom from '../widget/BreadcrumbCustom';
|
||||
import { Editor } from 'react-draft-wysiwyg';
|
||||
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
|
||||
import draftToHtml from 'draftjs-to-html';
|
||||
import draftToMarkdown from 'draftjs-to-markdown';
|
||||
import { EditorState } from 'draft-js';
|
||||
|
||||
const rawContentState = {
|
||||
entityMap: {
|
||||
'0': {
|
||||
type: 'IMAGE',
|
||||
mutability: 'MUTABLE',
|
||||
data: { src: 'http://i.imgur.com/aMtBIep.png', height: 'auto', width: '100%' },
|
||||
},
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
key: '9unl6',
|
||||
text: '',
|
||||
type: 'unstyled',
|
||||
depth: 0,
|
||||
inlineStyleRanges: [],
|
||||
entityRanges: [],
|
||||
data: {},
|
||||
},
|
||||
{
|
||||
key: '95kn',
|
||||
text: ' ',
|
||||
type: 'atomic',
|
||||
depth: 0,
|
||||
inlineStyleRanges: [],
|
||||
entityRanges: [{ offset: 0, length: 1, key: 0 }],
|
||||
data: {},
|
||||
},
|
||||
{
|
||||
key: '7rjes',
|
||||
text: '',
|
||||
type: 'unstyled',
|
||||
depth: 0,
|
||||
inlineStyleRanges: [],
|
||||
entityRanges: [],
|
||||
data: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
type WysiwygState = {
|
||||
editorContent: any;
|
||||
contentState: any;
|
||||
editorState: EditorState | undefined;
|
||||
};
|
||||
class Wysiwyg extends Component {
|
||||
state: WysiwygState = {
|
||||
editorContent: undefined,
|
||||
contentState: rawContentState,
|
||||
editorState: undefined,
|
||||
};
|
||||
|
||||
onEditorChange = (editorContent: any) => {
|
||||
this.setState({
|
||||
editorContent,
|
||||
});
|
||||
};
|
||||
|
||||
clearContent = () => {
|
||||
this.setState({
|
||||
contentState: '',
|
||||
});
|
||||
};
|
||||
|
||||
onContentStateChange = (contentState: any) => {
|
||||
console.log('contentState', contentState);
|
||||
};
|
||||
|
||||
onEditorStateChange = (editorState: any) => {
|
||||
this.setState({
|
||||
editorState,
|
||||
});
|
||||
};
|
||||
|
||||
imageUploadCallBack = (file: any) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest(); // eslint-disable-line no-undef
|
||||
xhr.open('POST', 'https://api.imgur.com/3/image');
|
||||
xhr.setRequestHeader('Authorization', 'Client-ID 8d26ccd12712fca');
|
||||
const data = new FormData(); // eslint-disable-line no-undef
|
||||
data.append('image', file);
|
||||
xhr.send(data);
|
||||
xhr.addEventListener('load', () => {
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
resolve(response);
|
||||
});
|
||||
xhr.addEventListener('error', () => {
|
||||
const error = JSON.parse(xhr.responseText);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
render() {
|
||||
const { editorContent, editorState } = this.state;
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom breads={['UI', '富文本']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="富文本编辑器" bordered={false}>
|
||||
<Editor
|
||||
editorState={editorState}
|
||||
toolbarClassName="home-toolbar"
|
||||
wrapperClassName="home-wrapper"
|
||||
editorClassName="home-editor"
|
||||
onEditorStateChange={this.onEditorStateChange}
|
||||
toolbar={{
|
||||
history: { inDropdown: true },
|
||||
inline: { inDropdown: false },
|
||||
list: { inDropdown: true },
|
||||
textAlign: { inDropdown: true },
|
||||
image: { uploadCallback: this.imageUploadCallBack },
|
||||
}}
|
||||
onContentStateChange={this.onEditorChange}
|
||||
placeholder="请输入正文~~尝试@哦,有惊喜"
|
||||
spellCheck
|
||||
onFocus={() => {
|
||||
console.log('focus');
|
||||
}}
|
||||
onBlur={() => {
|
||||
console.log('blur');
|
||||
}}
|
||||
onTab={() => {
|
||||
console.log('tab');
|
||||
return true;
|
||||
}}
|
||||
localization={{
|
||||
locale: 'zh',
|
||||
translations: { 'generic.add': 'Test-Add' },
|
||||
}}
|
||||
mention={{
|
||||
separator: ' ',
|
||||
trigger: '@',
|
||||
caseSensitive: true,
|
||||
suggestions: [
|
||||
{ text: 'A', value: 'AB', url: 'href-a' },
|
||||
{ text: 'AB', value: 'ABC', url: 'href-ab' },
|
||||
{ text: 'ABC', value: 'ABCD', url: 'href-abc' },
|
||||
{ text: 'ABCD', value: 'ABCDDDD', url: 'href-abcd' },
|
||||
{ text: 'ABCDE', value: 'ABCDE', url: 'href-abcde' },
|
||||
{ text: 'ABCDEF', value: 'ABCDEF', url: 'href-abcdef' },
|
||||
{
|
||||
text: 'ABCDEFG',
|
||||
value: 'ABCDEFG',
|
||||
url: 'href-abcdefg',
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
|
||||
<style>{`
|
||||
.home-editor {
|
||||
min-height: 300px;
|
||||
}
|
||||
`}</style>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={8}>
|
||||
<Card title="同步转换HTML" bordered={false}>
|
||||
<pre>{draftToHtml(editorContent)}</pre>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={8}>
|
||||
<Card title="同步转换MarkDown" bordered={false}>
|
||||
<pre style={{ whiteSpace: 'pre-wrap' }}>
|
||||
{draftToMarkdown(editorContent)}
|
||||
</pre>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={8}>
|
||||
<Card title="同步转换JSON" bordered={false}>
|
||||
<pre style={{ whiteSpace: 'normal' }}>
|
||||
{JSON.stringify(editorContent)}
|
||||
</pre>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Wysiwyg;
|
||||
57
src/components/ui/banners/AutoPlay.tsx
Normal file
57
src/components/ui/banners/AutoPlay.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/26.
|
||||
*/
|
||||
import React from 'react';
|
||||
import BannerAnim, { Element } from 'rc-banner-anim';
|
||||
import TweenOne from 'rc-tween-one';
|
||||
import 'rc-banner-anim/assets/index.css';
|
||||
const BgElement = Element.BgElement;
|
||||
class AutoPlay extends React.Component {
|
||||
render(){
|
||||
return (
|
||||
<BannerAnim prefixCls="banner-user" autoPlay>
|
||||
<Element
|
||||
prefixCls="banner-user-elem"
|
||||
key="0"
|
||||
>
|
||||
<BgElement
|
||||
key="bg"
|
||||
className="bg"
|
||||
style={{
|
||||
background: '#364D79',
|
||||
}}
|
||||
/>
|
||||
<TweenOne className="banner-user-title" animation={{ y: 30, opacity: 0, type: 'from' }}>
|
||||
Ant Motion Banner
|
||||
</TweenOne>
|
||||
<TweenOne className="banner-user-text"
|
||||
animation={{ y: 30, opacity: 0, type: 'from', delay: 100 }}
|
||||
>
|
||||
The Fast Way Use Animation In React
|
||||
</TweenOne>
|
||||
</Element>
|
||||
<Element
|
||||
prefixCls="banner-user-elem"
|
||||
key="1"
|
||||
>
|
||||
<BgElement
|
||||
key="bg"
|
||||
className="bg"
|
||||
style={{
|
||||
background: '#64CBCC',
|
||||
}}
|
||||
/>
|
||||
<TweenOne className="banner-user-title" animation={{ y: 30, opacity: 0, type: 'from' }}>
|
||||
Ant Motion Banner
|
||||
</TweenOne>
|
||||
<TweenOne className="banner-user-text"
|
||||
animation={{ y: 30, opacity: 0, type: 'from', delay: 100 }}
|
||||
>
|
||||
The Fast Way Use Animation In React
|
||||
</TweenOne>
|
||||
</Element>
|
||||
</BannerAnim>);
|
||||
}
|
||||
}
|
||||
|
||||
export default AutoPlay;
|
||||
60
src/components/ui/banners/Basic.tsx
Normal file
60
src/components/ui/banners/Basic.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/26.
|
||||
*/
|
||||
import React from 'react';
|
||||
import BannerAnim, { Element } from 'rc-banner-anim';
|
||||
import TweenOne from 'rc-tween-one';
|
||||
import 'rc-banner-anim/assets/index.css';
|
||||
const BgElement = Element.BgElement;
|
||||
class Basic extends React.Component {
|
||||
render(){
|
||||
return (
|
||||
<BannerAnim prefixCls="banner-user">
|
||||
<Element
|
||||
prefixCls="banner-user-elem"
|
||||
key="0"
|
||||
>
|
||||
<BgElement
|
||||
key="bg"
|
||||
className="bg"
|
||||
style={{
|
||||
background: '#364D79',
|
||||
}}
|
||||
/>
|
||||
<TweenOne className="banner-user-title" animation={{ y: 30, opacity: 0, type: 'from' }}>
|
||||
Ant Motion Banner
|
||||
</TweenOne>
|
||||
<TweenOne
|
||||
className="banner-user-text"
|
||||
animation={{ y: 30, opacity: 0, type: 'from', delay: 100 }}
|
||||
>
|
||||
The Fast Way Use Animation In React
|
||||
</TweenOne>
|
||||
</Element>
|
||||
<Element
|
||||
prefixCls="banner-user-elem"
|
||||
key="1"
|
||||
>
|
||||
<BgElement
|
||||
key="bg"
|
||||
className="bg"
|
||||
style={{
|
||||
background: '#64CBCC',
|
||||
}}
|
||||
/>
|
||||
<TweenOne className="banner-user-title" animation={{ y: 30, opacity: 0, type: 'from' }}>
|
||||
Ant Motion Banner
|
||||
</TweenOne>
|
||||
<TweenOne
|
||||
className="banner-user-text"
|
||||
animation={{ y: 30, opacity: 0, type: 'from', delay: 100 }}
|
||||
>
|
||||
The Fast Way Use Animation In React
|
||||
</TweenOne>
|
||||
</Element>
|
||||
</BannerAnim>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Basic;
|
||||
208
src/components/ui/banners/Custom.tsx
Normal file
208
src/components/ui/banners/Custom.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/26.
|
||||
*/
|
||||
import React from 'react';
|
||||
import BannerAnim from 'rc-banner-anim';
|
||||
import TweenOne, { TweenOneGroup } from 'rc-tween-one';
|
||||
import 'rc-banner-anim/assets/index.css';
|
||||
const { Element, Arrow, Thumb } = BannerAnim;
|
||||
const BgElement = Element.BgElement;
|
||||
type CustomState = {
|
||||
intShow: number;
|
||||
prevEnter: boolean;
|
||||
nextEnter: boolean;
|
||||
thumbEnter: boolean;
|
||||
};
|
||||
class Custom extends React.Component<any, CustomState> {
|
||||
imgArray: string[];
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.imgArray = [
|
||||
'https://zos.alipayobjects.com/rmsportal/hzPBTkqtFpLlWCi.jpg',
|
||||
'https://zos.alipayobjects.com/rmsportal/gGlUMYGEIvjDOOw.jpg',
|
||||
];
|
||||
this.state = {
|
||||
intShow: 0,
|
||||
prevEnter: false,
|
||||
nextEnter: false,
|
||||
thumbEnter: false,
|
||||
};
|
||||
}
|
||||
|
||||
onChange = (type: string, int: number) => {
|
||||
if (type === 'before') {
|
||||
this.setState({
|
||||
intShow: int,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getNextPrevNumber() {
|
||||
let nextInt = this.state.intShow + 1;
|
||||
let prevInt = this.state.intShow - 1;
|
||||
if (nextInt >= this.imgArray.length) {
|
||||
nextInt = 0;
|
||||
}
|
||||
if (prevInt < 0) {
|
||||
prevInt = this.imgArray.length - 1;
|
||||
}
|
||||
|
||||
return [prevInt, nextInt];
|
||||
}
|
||||
|
||||
prevEnter() {
|
||||
this.setState({
|
||||
prevEnter: true,
|
||||
});
|
||||
}
|
||||
|
||||
prevLeave() {
|
||||
this.setState({
|
||||
prevEnter: false,
|
||||
});
|
||||
}
|
||||
|
||||
nextEnter() {
|
||||
this.setState({
|
||||
nextEnter: true,
|
||||
});
|
||||
}
|
||||
|
||||
nextLeave() {
|
||||
this.setState({
|
||||
nextEnter: false,
|
||||
});
|
||||
}
|
||||
|
||||
onMouseEnter() {
|
||||
this.setState({
|
||||
thumbEnter: true,
|
||||
});
|
||||
}
|
||||
|
||||
onMouseLeave() {
|
||||
this.setState({
|
||||
thumbEnter: false,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const intArray = this.getNextPrevNumber();
|
||||
const thumbChildren = this.imgArray.map((img, i) => (
|
||||
<span key={i}>
|
||||
<i style={{ backgroundImage: `url(${img})` }} />
|
||||
</span>
|
||||
));
|
||||
return (
|
||||
<BannerAnim
|
||||
onChange={this.onChange}
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
prefixCls="custom-arrow-thumb"
|
||||
>
|
||||
<Element key="aaa" prefixCls="banner-user-elem">
|
||||
<BgElement
|
||||
key="bg"
|
||||
className="bg"
|
||||
style={{
|
||||
backgroundImage: `url(${this.imgArray[0]})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
/>
|
||||
<TweenOne
|
||||
className="banner-user-title"
|
||||
animation={{ y: 30, opacity: 0, type: 'from' }}
|
||||
>
|
||||
Ant Motion Banner
|
||||
</TweenOne>
|
||||
<TweenOne
|
||||
className="banner-user-text"
|
||||
animation={{ y: 30, opacity: 0, type: 'from', delay: 100 }}
|
||||
>
|
||||
The Fast Way Use Animation In React
|
||||
</TweenOne>
|
||||
</Element>
|
||||
<Element key="bbb" prefixCls="banner-user-elem">
|
||||
<BgElement
|
||||
key="bg"
|
||||
className="bg"
|
||||
style={{
|
||||
backgroundImage: `url(${this.imgArray[1]})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
/>
|
||||
<TweenOne
|
||||
className="banner-user-title"
|
||||
animation={{ y: 30, opacity: 0, type: 'from' }}
|
||||
>
|
||||
Ant Motion Banner
|
||||
</TweenOne>
|
||||
<TweenOne
|
||||
className="banner-user-text"
|
||||
animation={{ y: 30, opacity: 0, type: 'from', delay: 100 }}
|
||||
>
|
||||
The Fast Way Use Animation In React
|
||||
</TweenOne>
|
||||
</Element>
|
||||
<Arrow
|
||||
arrowType="prev"
|
||||
key="prev"
|
||||
prefixCls="user-arrow"
|
||||
component={TweenOne}
|
||||
onMouseEnter={this.prevEnter}
|
||||
onMouseLeave={this.prevLeave}
|
||||
// animation={{ left: this.state.prevEnter ? 0 : -120 }}
|
||||
>
|
||||
<div className="arrow" />
|
||||
<TweenOneGroup
|
||||
enter={{ opacity: 0, type: 'from' }}
|
||||
leave={{ opacity: 0 }}
|
||||
appear={false}
|
||||
className="img-wrapper"
|
||||
component="ul"
|
||||
>
|
||||
<li
|
||||
style={{ backgroundImage: `url(${this.imgArray[intArray[0]]})` }}
|
||||
key={intArray[0]}
|
||||
/>
|
||||
</TweenOneGroup>
|
||||
</Arrow>
|
||||
<Arrow
|
||||
arrowType="next"
|
||||
key="next"
|
||||
prefixCls="user-arrow"
|
||||
component={TweenOne}
|
||||
onMouseEnter={this.nextEnter}
|
||||
onMouseLeave={this.nextLeave}
|
||||
// animation={{ right: this.state.nextEnter ? 0 : -120 }}
|
||||
>
|
||||
<div className="arrow" />
|
||||
<TweenOneGroup
|
||||
enter={{ opacity: 0, type: 'from' }}
|
||||
leave={{ opacity: 0 }}
|
||||
appear={false}
|
||||
className="img-wrapper"
|
||||
component="ul"
|
||||
>
|
||||
<li
|
||||
style={{ backgroundImage: `url(${this.imgArray[intArray[1]]})` }}
|
||||
key={intArray[1]}
|
||||
/>
|
||||
</TweenOneGroup>
|
||||
</Arrow>
|
||||
<Thumb
|
||||
prefixCls="user-thumb"
|
||||
key="thumb"
|
||||
component={TweenOne}
|
||||
// animation={{ bottom: this.state.thumbEnter ? 0 : -70 }}
|
||||
>
|
||||
{thumbChildren}
|
||||
</Thumb>
|
||||
</BannerAnim>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Custom;
|
||||
45
src/components/ui/banners/index.tsx
Normal file
45
src/components/ui/banners/index.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/26.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import BreadcrumbCustom from '../../widget/BreadcrumbCustom';
|
||||
import Basic from './Basic';
|
||||
import AutoPlay from './AutoPlay';
|
||||
// import Custom from './Custom';
|
||||
|
||||
class Banners extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="gutter-example button-demo">
|
||||
<BreadcrumbCustom breads={['UI', '轮播图']} />
|
||||
<Row gutter={16}>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="基本用法" bordered={false}>
|
||||
<Basic />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="自动轮播-默认5秒" bordered={false}>
|
||||
<AutoPlay />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="gutter-row" md={24}>
|
||||
<div className="gutter-box">
|
||||
<Card title="自定义左右箭头与缩略图" bordered={false}>
|
||||
{/*引入自定义会导致组件冲突不显示*/}
|
||||
{/*<Custom />*/}
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Banners;
|
||||
88
src/components/ui/emoji/iconfont.ts
Normal file
88
src/components/ui/emoji/iconfont.ts
Normal file
File diff suppressed because one or more lines are too long
38
src/components/ui/emoji/index.tsx
Normal file
38
src/components/ui/emoji/index.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/22.
|
||||
*/
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import setFont from './iconfont';
|
||||
|
||||
setFont();
|
||||
type EmojiProps = {
|
||||
type: string;
|
||||
};
|
||||
const Emoji = ({ type }: EmojiProps) => {
|
||||
const useTag = `<use xlink:href=${'#icon-' + type} />`;
|
||||
return (
|
||||
<i className="emoji">
|
||||
<svg className="emoji" dangerouslySetInnerHTML={{ __html: useTag }} />
|
||||
<style>{`
|
||||
.emoji {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
}
|
||||
.emoji svg {
|
||||
width: 3em;
|
||||
height: 3em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
`}</style>
|
||||
</i>
|
||||
);
|
||||
};
|
||||
|
||||
Emoji.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default Emoji;
|
||||
21
src/components/ui/map/Tencent.tsx
Normal file
21
src/components/ui/map/Tencent.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import ReactQMap from 'react-qmap';
|
||||
|
||||
const getContianer = (dom: any) => {
|
||||
const middleControl = document.createElement('div');
|
||||
middleControl.style.cssText =
|
||||
'width: 22px;height: 30px;position: absolute;left: 50%;top: 50%;z-index: 999;margin-left: -23px;margin-top: -23px;';
|
||||
middleControl.innerHTML = `<img src="${require('../../../style/imgs/spot_location.png')}" style="width: 100%;height: 100%;" />`;
|
||||
dom.appendChild(middleControl);
|
||||
};
|
||||
|
||||
export default () => (
|
||||
<ReactQMap
|
||||
center={{ latitude: 30.53786, longitude: 104.07265 }}
|
||||
initialOptions={{ zoomControl: true, mapTypeControl: true }}
|
||||
apiKey="UN6BZ-MP2W6-XWCSX-M2ATU-QORGZ-OWFOE"
|
||||
style={{ height: 500 }}
|
||||
mySpot={{ latitude: 30.53786, longitude: 104.07265 }}
|
||||
getContainer={getContianer}
|
||||
/>
|
||||
);
|
||||
19
src/components/ui/map/index.tsx
Normal file
19
src/components/ui/map/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import Tencent from './Tencent';
|
||||
import { Row, Col, Card } from 'antd';
|
||||
import BreadcrumbCustom from '../../widget/BreadcrumbCustom';
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<BreadcrumbCustom breads={['UI', '地图']} />
|
||||
<Row gutter={16}>
|
||||
<Col md={24}>
|
||||
<div style={{ height: 500 }}>
|
||||
<Card bordered={false} title="腾讯地图">
|
||||
<Tencent />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
19
src/components/widget/AuthWidget.tsx
Normal file
19
src/components/widget/AuthWidget.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Created by 叶子 on 2017/7/31.
|
||||
*/
|
||||
import { Component } from 'react';
|
||||
import { connectAlita } from 'redux-alita';
|
||||
|
||||
type AuthWidgetProps = {
|
||||
auth: any;
|
||||
children: (param: any) => React.ReactElement;
|
||||
};
|
||||
|
||||
class AuthWidget extends Component<AuthWidgetProps> {
|
||||
render() {
|
||||
const { auth = {} } = this.props;
|
||||
return this.props.children(auth.data || {});
|
||||
}
|
||||
}
|
||||
|
||||
export default connectAlita(['auth'])(AuthWidget);
|
||||
25
src/components/widget/BreadcrumbCustom.tsx
Normal file
25
src/components/widget/BreadcrumbCustom.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/22.
|
||||
*/
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Breadcrumb } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
interface BreadcrumbCustomProps {
|
||||
breads?: ReactNode[];
|
||||
}
|
||||
const BreadcrumbCustom = (props: BreadcrumbCustomProps) => {
|
||||
const { breads } = props;
|
||||
return (
|
||||
<Breadcrumb style={{ margin: '12px 0' }}>
|
||||
<Breadcrumb.Item>
|
||||
<Link to={'/app/dashboard/index'}>首页</Link>
|
||||
</Breadcrumb.Item>
|
||||
{breads?.map((bread, i) => (
|
||||
<Breadcrumb.Item key={i}>{bread}</Breadcrumb.Item>
|
||||
))}
|
||||
</Breadcrumb>
|
||||
);
|
||||
};
|
||||
|
||||
export default BreadcrumbCustom;
|
||||
19
src/components/widget/Copyright.tsx
Normal file
19
src/components/widget/Copyright.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* File: Copyright.tsx
|
||||
* Desc: 版权信息
|
||||
* File Created: 2020-04-12 22:50:33
|
||||
* Author: chenghao
|
||||
* ------
|
||||
* Copyright 2020 - present, karakal
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
const Copyright = () => {
|
||||
return (
|
||||
<div>
|
||||
react-admin ©{new Date().getFullYear()} Created by yezihaohao@yezi.haohao@foxmail.com
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Copyright;
|
||||
9
src/components/widget/Loading.tsx
Normal file
9
src/components/widget/Loading.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default () => (
|
||||
<div
|
||||
style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%' }}
|
||||
>
|
||||
页面加载中...
|
||||
</div>
|
||||
);
|
||||
62
src/components/widget/PwaInstaller.tsx
Normal file
62
src/components/widget/PwaInstaller.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* File: PwaInstaller.js
|
||||
* Desc: pwa手动触发安装
|
||||
* File Created: 2018-11-07 21:13:18
|
||||
* Author: chenghao
|
||||
*
|
||||
* Copyright 2018 - present, chenghao
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class PwaInstaller extends Component {
|
||||
state = {
|
||||
installed: true,
|
||||
};
|
||||
componentDidMount() {
|
||||
window.addEventListener('beforeinstallprompt', this.beforeInstallPrompt);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('beforeinstallprompt', this.beforeInstallPrompt);
|
||||
}
|
||||
deferredPrompt: any;
|
||||
beforeInstallPrompt = (e: Event) => {
|
||||
console.log('beforeinstallprompt Event fired');
|
||||
// 未安装PWA应用
|
||||
this.setState({ installed: false });
|
||||
|
||||
e.preventDefault();
|
||||
// Stash the event so it can be triggered later.
|
||||
this.deferredPrompt = e;
|
||||
return false;
|
||||
};
|
||||
download = () => {
|
||||
if (this.deferredPrompt !== undefined) {
|
||||
// The user has had a postive interaction with our app and Chrome
|
||||
// has tried to prompt previously, so let's show the prompt.
|
||||
this.deferredPrompt.prompt();
|
||||
// Follow what the user has done with the prompt.
|
||||
this.deferredPrompt.userChoice.then((choiceResult: any) => {
|
||||
console.log(choiceResult.outcome);
|
||||
if (choiceResult.outcome === 'dismissed') {
|
||||
console.log('User cancelled home screen install');
|
||||
} else {
|
||||
console.log('User added to home screen');
|
||||
}
|
||||
// We no longer need the prompt. Clear it up.
|
||||
this.deferredPrompt = null;
|
||||
});
|
||||
}
|
||||
};
|
||||
render() {
|
||||
const { installed } = this.state;
|
||||
return (
|
||||
!installed && (
|
||||
<div className="installer" onClick={this.download}>
|
||||
<div className="installer__btn" />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PwaInstaller;
|
||||
43
src/components/widget/ThemePicker.tsx
Normal file
43
src/components/widget/ThemePicker.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React, { Component } from 'react';
|
||||
import { SketchPicker } from 'react-color';
|
||||
import classNames from 'classnames';
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
|
||||
class ThemePicker extends Component {
|
||||
state = {
|
||||
switcherOn: false,
|
||||
background: localStorage.getItem('@primary-color') || '#313653',
|
||||
};
|
||||
_switcherOn = () => {
|
||||
this.setState({
|
||||
switcherOn: !this.state.switcherOn,
|
||||
});
|
||||
};
|
||||
_handleChangeComplete = (color: any) => {
|
||||
console.log(color);
|
||||
this.setState({ background: color.hex });
|
||||
localStorage.setItem('@primary-color', color.hex);
|
||||
|
||||
(window as any).less.modifyVars({
|
||||
'@primary-color': color.hex,
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const { switcherOn, background } = this.state;
|
||||
return (
|
||||
<div className={classNames('switcher dark-white', { active: switcherOn })}>
|
||||
<span className="sw-btn dark-white" onClick={this._switcherOn}>
|
||||
<SettingOutlined type="setting" className="text-dark" />
|
||||
</span>
|
||||
<div style={{ padding: 10 }} className="clear">
|
||||
<SketchPicker
|
||||
color={background}
|
||||
onChangeComplete={this._handleChangeComplete}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ThemePicker;
|
||||
4
src/components/widget/index.tsx
Normal file
4
src/components/widget/index.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
export { default as PwaInstaller } from './PwaInstaller';
|
||||
export { default as AuthWidget } from './AuthWidget';
|
||||
export { default as ThemePicker } from './ThemePicker';
|
||||
export { default as Copyright } from './Copyright';
|
||||
60
src/index.tsx
Normal file
60
src/index.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AlitaProvider, setConfig } from 'redux-alita';
|
||||
import umbrella from 'umbrella-storage';
|
||||
import Page from './Page';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
import * as apis from './service';
|
||||
// import { AppContainer } from 'react-hot-loader';
|
||||
import './style/lib/animate.css';
|
||||
import './style/index.less';
|
||||
import './style/antd/index.less';
|
||||
|
||||
setConfig(apis);
|
||||
umbrella.config('REACT-ADMIN');
|
||||
// const render = Component => { // 增加react-hot-loader保持状态刷新操作,如果不需要可去掉并把下面注释的打开
|
||||
// ReactDOM.render(
|
||||
// <AppContainer>
|
||||
// <Provider store={store}>
|
||||
// <Component store={store} />
|
||||
// </Provider>
|
||||
// </AppContainer>
|
||||
// ,
|
||||
// document.getElementById('root')
|
||||
// );
|
||||
// };
|
||||
|
||||
// render(Page);
|
||||
|
||||
// Webpack Hot Module Replacement API
|
||||
// if (module.hot) {
|
||||
// // 隐藏You cannot change <Router routes>; it will be ignored 错误提示
|
||||
// // react-hot-loader 使用在react-router 3.x上引起的提示,react-router 4.x不存在
|
||||
// // 详情可参照https://github.com/gaearon/react-hot-loader/issues/298
|
||||
// const orgError = console.error; // eslint-disable-line no-console
|
||||
// console.error = (...args) => { // eslint-disable-line no-console
|
||||
// if (args && args.length === 1 && typeof args[0] === 'string' && args[0].indexOf('You cannot change <Router routes>;') > -1) {
|
||||
// // React route changed
|
||||
// } else {
|
||||
// // Log the error as normally
|
||||
// orgError.apply(console, args);
|
||||
// }
|
||||
// };
|
||||
// module.hot.accept('./Page', () => {
|
||||
// render(Page);
|
||||
// })
|
||||
// }
|
||||
|
||||
ReactDOM.render(
|
||||
// <AppContainer>
|
||||
<AlitaProvider>
|
||||
<Page />
|
||||
</AlitaProvider>,
|
||||
// </AppContainer>
|
||||
document.getElementById('root')
|
||||
);
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: http://bit.ly/CRA-PWA
|
||||
// serviceWorker.unregister();
|
||||
serviceWorker.register();
|
||||
1
src/logo.svg
Normal file
1
src/logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 8.0 KiB |
75
src/react-app-env.d.ts
vendored
Normal file
75
src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="react" />
|
||||
/// <reference types="react-dom" />
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
readonly NODE_ENV: 'development' | 'production' | 'test';
|
||||
readonly PUBLIC_URL: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '*.bmp' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.gif' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.jpg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.jpeg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.png' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.webp' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.svg' {
|
||||
import * as React from 'react';
|
||||
|
||||
export const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
|
||||
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.module.css' {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module '*.module.scss' {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module '*.module.sass' {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module '*.module.less' {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module 'draftjs-to-html';
|
||||
|
||||
declare module 'draftjs-to-markdown';
|
||||
|
||||
declare module 'react-qmap';
|
||||
48
src/routes/RouteWrapper.tsx
Normal file
48
src/routes/RouteWrapper.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* File: RouteWrapper.tsx
|
||||
* Desc: 描述
|
||||
* File Created: 2020-05-19 11:32:58
|
||||
* Author: chenghao at <hao.cheng@karakal.com.cn>
|
||||
* ------
|
||||
* Copyright 2020 - present, karakal
|
||||
*/
|
||||
import React, { useMemo } from 'react';
|
||||
import DocumentTitle from 'react-document-title';
|
||||
import queryString from 'query-string';
|
||||
|
||||
const RouteWrapper = (props: any) => {
|
||||
let { Comp, route, ...restProps } = props;
|
||||
/** useMemo 缓存query,避免每次生成生的query */
|
||||
const queryMemo = useMemo(() => {
|
||||
const queryReg = /\?\S*/g;
|
||||
const matchQuery = (reg: RegExp) => {
|
||||
const queryParams = restProps.location.search.match(reg);
|
||||
return queryParams ? queryParams[0] : '{}';
|
||||
};
|
||||
return queryString.parse(matchQuery(queryReg));
|
||||
}, [restProps.location.search]);
|
||||
const mergeQueryToProps = () => {
|
||||
const queryReg = /\?\S*/g;
|
||||
const removeQueryInRouter = (restProps: any, reg: RegExp) => {
|
||||
const { params } = restProps.match;
|
||||
Object.keys(params).forEach((key) => {
|
||||
params[key] = params[key] && params[key].replace(reg, '');
|
||||
});
|
||||
restProps.match.params = { ...params };
|
||||
};
|
||||
|
||||
restProps = removeQueryInRouter(restProps, queryReg);
|
||||
const merge = {
|
||||
...restProps,
|
||||
query: queryMemo,
|
||||
};
|
||||
return merge;
|
||||
};
|
||||
return (
|
||||
<DocumentTitle title={route.title}>
|
||||
<Comp {...mergeQueryToProps()} />
|
||||
</DocumentTitle>
|
||||
);
|
||||
};
|
||||
|
||||
export default RouteWrapper;
|
||||
157
src/routes/config.ts
Normal file
157
src/routes/config.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
export interface IFMenuBase {
|
||||
key: string;
|
||||
title: string;
|
||||
icon?: string;
|
||||
component?: string;
|
||||
query?: string;
|
||||
requireAuth?: string;
|
||||
route?: string;
|
||||
/** 是否登录校验,true不进行校验(访客) */
|
||||
login?: boolean;
|
||||
}
|
||||
|
||||
export interface IFMenu extends IFMenuBase {
|
||||
subs?: IFMenu[];
|
||||
}
|
||||
|
||||
const menus: {
|
||||
menus: IFMenu[];
|
||||
others: IFMenu[] | [];
|
||||
[index: string]: any;
|
||||
} = {
|
||||
menus: [
|
||||
// 菜单相关路由
|
||||
{ key: '/app/dashboard/index', title: '首页', icon: 'mobile', component: 'Dashboard' },
|
||||
{
|
||||
key: '/app/ui',
|
||||
title: 'UI',
|
||||
icon: 'scan',
|
||||
subs: [
|
||||
{ key: '/app/ui/buttons', title: '按钮', component: 'Buttons' },
|
||||
{ key: '/app/ui/icons', title: '图标', component: 'Icons' },
|
||||
{ key: '/app/ui/spins', title: '加载中', component: 'Spins' },
|
||||
{ key: '/app/ui/modals', title: '对话框', component: 'Modals' },
|
||||
{ key: '/app/ui/notifications', title: '通知提醒框', component: 'Notifications' },
|
||||
{ key: '/app/ui/tabs', title: '标签页', component: 'Tabs' },
|
||||
{ key: '/app/ui/banners', title: '轮播图', component: 'Banners' },
|
||||
{ key: '/app/ui/wysiwyg', title: '富文本', component: 'WysiwygBundle' },
|
||||
{ key: '/app/ui/drags', title: '拖拽', component: 'Drags' },
|
||||
{ key: '/app/ui/gallery', title: '画廊', component: 'Gallery' },
|
||||
{ key: '/app/ui/map', title: '地图', component: 'MapUi' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '/app/animation',
|
||||
title: '动画',
|
||||
icon: 'rocket',
|
||||
subs: [
|
||||
{
|
||||
key: '/app/animation/basicAnimations',
|
||||
title: '基础动画',
|
||||
component: 'BasicAnimations',
|
||||
},
|
||||
{
|
||||
key: '/app/animation/exampleAnimations',
|
||||
title: '动画案例',
|
||||
component: 'ExampleAnimations',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '/app/table',
|
||||
title: '表格',
|
||||
icon: 'copy',
|
||||
subs: [
|
||||
{ key: '/app/table/basicTable', title: '基础表格', component: 'BasicTable' },
|
||||
{ key: '/app/table/advancedTable', title: '高级表格', component: 'AdvancedTable' },
|
||||
{
|
||||
key: '/app/table/asynchronousTable',
|
||||
title: '异步表格',
|
||||
component: 'AsynchronousTable',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '/app/chart',
|
||||
title: '图表',
|
||||
icon: 'area-chart',
|
||||
subs: [
|
||||
{ key: '/app/chart/echarts', title: 'echarts', component: 'Echarts' },
|
||||
{ key: '/app/chart/recharts', title: 'recharts', component: 'Recharts' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '/subs4',
|
||||
title: '页面',
|
||||
icon: 'switcher',
|
||||
subs: [
|
||||
{ key: '/login', title: '登录' },
|
||||
{ key: '/404', title: '404' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '/app/auth',
|
||||
title: '权限管理',
|
||||
icon: 'safety',
|
||||
subs: [
|
||||
{ key: '/app/auth/basic', title: '基础演示', component: 'AuthBasic' },
|
||||
{
|
||||
key: '/app/auth/routerEnter',
|
||||
title: '路由拦截',
|
||||
component: 'RouterEnter',
|
||||
requireAuth: 'auth/testPage',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '/app/cssModule',
|
||||
title: 'cssModule',
|
||||
icon: 'star',
|
||||
component: 'Cssmodule',
|
||||
},
|
||||
{
|
||||
key: '/app/extension',
|
||||
title: '功能扩展',
|
||||
icon: 'bars',
|
||||
subs: [
|
||||
{
|
||||
key: '/app/extension/queryParams',
|
||||
title: '问号形式参数',
|
||||
component: 'QueryParams',
|
||||
query: '?param1=1¶m2=2',
|
||||
},
|
||||
{
|
||||
key: '/app/extension/visitor',
|
||||
title: '访客模式',
|
||||
component: 'Visitor',
|
||||
login: true,
|
||||
},
|
||||
{
|
||||
key: '/app/extension/multiple',
|
||||
title: '多级菜单',
|
||||
subs: [
|
||||
{
|
||||
key: '/app/extension/multiple/child',
|
||||
title: '多级菜单子菜单',
|
||||
subs: [
|
||||
{
|
||||
key: '/app/extension/multiple/child/child',
|
||||
title: '多级菜单子子菜单',
|
||||
component: 'MultipleMenu',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '/app/extension/env',
|
||||
title: '环境配置',
|
||||
component: 'Env',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
others: [], // 非菜单相关路由
|
||||
};
|
||||
|
||||
export default menus;
|
||||
72
src/routes/index.tsx
Normal file
72
src/routes/index.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Created by 叶子 on 2017/8/13.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Route, Redirect, Switch } from 'react-router-dom';
|
||||
import { useAlita } from 'redux-alita';
|
||||
import umbrella from 'umbrella-storage';
|
||||
import AllComponents from '../components';
|
||||
import routesConfig, { IFMenuBase, IFMenu } from './config';
|
||||
import { checkLogin } from '../utils';
|
||||
import RouteWrapper from './RouteWrapper';
|
||||
|
||||
type CRouterProps = {
|
||||
auth: any;
|
||||
};
|
||||
|
||||
const CRouter = (props: CRouterProps) => {
|
||||
const { auth } = props;
|
||||
const [smenus] = useAlita({ smenus: null }, { light: true });
|
||||
|
||||
const getPermits = (): any[] | null => {
|
||||
return auth ? auth.permissions : null;
|
||||
};
|
||||
const requireAuth = (permit: any, component: React.ReactElement) => {
|
||||
const permits = getPermits();
|
||||
if (!permits || !permits.includes(permit)) return <Redirect to={'404'} />;
|
||||
return component;
|
||||
};
|
||||
const requireLogin = (component: React.ReactElement, permit: any) => {
|
||||
const permits = getPermits();
|
||||
if (!checkLogin(permits)) {
|
||||
// 线上环境判断是否登录
|
||||
return <Redirect to={'/login'} />;
|
||||
}
|
||||
return permit ? requireAuth(permit, component) : component;
|
||||
};
|
||||
const createMenu = (r: IFMenu) => {
|
||||
const route = (r: IFMenuBase) => {
|
||||
const Component = r.component && AllComponents[r.component];
|
||||
return (
|
||||
<Route
|
||||
key={r.route || r.key}
|
||||
exact
|
||||
path={r.route || r.key}
|
||||
render={(props: any) => {
|
||||
// 重新包装组件
|
||||
const wrapper = (
|
||||
<RouteWrapper {...{ ...props, Comp: Component, route: r }} />
|
||||
);
|
||||
return r.login ? wrapper : requireLogin(wrapper, r.requireAuth);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const subRoute = (r: IFMenu): any =>
|
||||
r.subs && r.subs.map((subR: IFMenu) => (subR.subs ? subRoute(subR) : route(subR)));
|
||||
|
||||
return r.component ? route(r) : subRoute(r);
|
||||
};
|
||||
const createRoute = (key: string) => routesConfig[key].map(createMenu);
|
||||
const getAsyncMenus = () => smenus || umbrella.getLocalStorage('smenus') || [];
|
||||
return (
|
||||
<Switch>
|
||||
{Object.keys(routesConfig).map((key) => createRoute(key))}
|
||||
{getAsyncMenus().map(createMenu)}
|
||||
<Route render={() => <Redirect to="/404" />} />
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
||||
export default CRouter;
|
||||
22
src/service/config.ts
Normal file
22
src/service/config.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Created by 叶子 on 2017/7/30.
|
||||
* 接口地址配置文件
|
||||
*/
|
||||
|
||||
//easy-mock模拟数据接口地址
|
||||
const MOCK_API = 'https://react-admin-mock.now.sh/api';
|
||||
export const MOCK_AUTH_ADMIN = MOCK_API + '/admin.js'; // 管理员权限接口
|
||||
export const MOCK_AUTH_VISITOR = MOCK_API + '/visitor.js'; // 访问权限接口
|
||||
/** 服务端异步菜单接口 */
|
||||
export const MOCK_MENU = MOCK_API + '/menu.js';
|
||||
|
||||
// github授权
|
||||
export const GIT_OAUTH = 'https://github.com/login/oauth';
|
||||
// github用户
|
||||
export const GIT_USER = 'https://api.github.com/user';
|
||||
|
||||
// bbc top news
|
||||
export const NEWS_BBC =
|
||||
'https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=429904aa01f54a39a278a406acf50070';
|
||||
|
||||
export const TRAILWAY_API = 'http://localhost:8000';
|
||||
49
src/service/index.ts
Normal file
49
src/service/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Created by hao.cheng on 2017/4/16.
|
||||
*/
|
||||
import axios from 'axios';
|
||||
import { get, post } from './tools';
|
||||
import * as config from './config';
|
||||
|
||||
export const getBbcNews = () => get({ url: config.NEWS_BBC });
|
||||
|
||||
export const npmDependencies = () =>
|
||||
axios
|
||||
.get('./npm.json')
|
||||
.then((res) => res.data)
|
||||
.catch((err) => console.log(err));
|
||||
|
||||
export const weibo = () =>
|
||||
axios
|
||||
.get('./weibo.json')
|
||||
.then((res) => res.data)
|
||||
.catch((err) => console.log(err));
|
||||
|
||||
export const gitOauthLogin = () =>
|
||||
get({
|
||||
url: `${config.GIT_OAUTH}/authorize?client_id=792cdcd244e98dcd2dee&redirect_uri=http://localhost:3006/&scope=user&state=reactAdmin`,
|
||||
});
|
||||
export const gitOauthToken = (code: string) =>
|
||||
post({
|
||||
url: `https://cors-anywhere.herokuapp.com/${config.GIT_OAUTH}/access_token`,
|
||||
data: {
|
||||
client_id: '792cdcd244e98dcd2dee',
|
||||
client_secret: '81c4ff9df390d482b7c8b214a55cf24bf1f53059',
|
||||
redirect_uri: 'http://localhost:3006/',
|
||||
state: 'reactAdmin',
|
||||
code,
|
||||
},
|
||||
});
|
||||
// {headers: {Accept: 'application/json'}}
|
||||
export const gitOauthInfo = (access_token: string) =>
|
||||
get({ url: `${config.GIT_USER}access_token=${access_token}` });
|
||||
|
||||
// easy-mock数据交互
|
||||
// 管理员权限获取
|
||||
export const admin = () => get({ url: config.MOCK_AUTH_ADMIN });
|
||||
// 访问权限获取
|
||||
export const guest = () => get({ url: config.MOCK_AUTH_VISITOR });
|
||||
/** 获取服务端菜单 */
|
||||
export const fetchMenu = () => get({ url: config.MOCK_MENU });
|
||||
|
||||
export const trailwayLogin = (data: any) => post({ url: config.TRAILWAY_API + '/login', data });
|
||||
43
src/service/tools.ts
Normal file
43
src/service/tools.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Created by 叶子 on 2017/7/30.
|
||||
* http通用工具函数
|
||||
*/
|
||||
import axios from 'axios';
|
||||
import { message } from 'antd';
|
||||
|
||||
interface IFRequestParam {
|
||||
url: string;
|
||||
msg?: string;
|
||||
config?: any;
|
||||
data?: any;
|
||||
}
|
||||
/**
|
||||
* 公用get请求
|
||||
* @param url 接口地址
|
||||
* @param msg 接口异常提示
|
||||
* @param headers 接口所需header配置
|
||||
*/
|
||||
export const get = ({ url, msg = '接口异常', config }: IFRequestParam) =>
|
||||
axios
|
||||
.get(url, config)
|
||||
.then((res) => res.data)
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
message.warn(msg);
|
||||
});
|
||||
|
||||
/**
|
||||
* 公用post请求
|
||||
* @param url 接口地址
|
||||
* @param data 接口参数
|
||||
* @param msg 接口异常提示
|
||||
* @param headers 接口所需header配置
|
||||
*/
|
||||
export const post = ({ url, data, msg = '接口异常', config }: IFRequestParam) =>
|
||||
axios
|
||||
.post(url, data, config)
|
||||
.then((res) => res.data)
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
message.warn(msg);
|
||||
});
|
||||
143
src/serviceWorker.ts
Normal file
143
src/serviceWorker.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
type Config = {
|
||||
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
||||
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
||||
};
|
||||
|
||||
export function register(config?: Config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(
|
||||
(process as { env: { [key: string]: string } }).env.PUBLIC_URL,
|
||||
window.location.href
|
||||
);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl: string, config?: Config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
||||
16
src/style/antd/header.less
Normal file
16
src/style/antd/header.less
Normal file
@@ -0,0 +1,16 @@
|
||||
.header {
|
||||
padding: 0;
|
||||
height: 65px;
|
||||
.ant-menu {
|
||||
background: transparent;
|
||||
color: @white;
|
||||
.ant-menu-item {
|
||||
&:hover {
|
||||
color: @white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.header__trigger {
|
||||
color: @white;
|
||||
}
|
||||
6
src/style/antd/index.less
Normal file
6
src/style/antd/index.less
Normal file
@@ -0,0 +1,6 @@
|
||||
@import './variables.less';
|
||||
@import './menu.less';
|
||||
@import './utils.less';
|
||||
@import './header.less';
|
||||
@import './layout.less';
|
||||
@import './reset.less';
|
||||
14
src/style/antd/layout.less
Normal file
14
src/style/antd/layout.less
Normal file
@@ -0,0 +1,14 @@
|
||||
.ant-layout-content {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.ant-layout {
|
||||
&.ant-layout-has-sider {
|
||||
&.app_layout-mobile {
|
||||
flex-direction: column;
|
||||
.ant-layout-content {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/style/antd/menu.less
Normal file
48
src/style/antd/menu.less
Normal file
@@ -0,0 +1,48 @@
|
||||
.ant-menu-root {
|
||||
&.ant-menu-inline, &.ant-menu-vertical {
|
||||
background: @primary-color;
|
||||
border-right: 1px solid @primary-color;
|
||||
color: @white;
|
||||
a {
|
||||
color: @white;
|
||||
}
|
||||
.ant-menu-submenu-selected, {
|
||||
color: @white;
|
||||
}
|
||||
.ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open, .ant-menu-submenu-active {
|
||||
color: @white;
|
||||
}
|
||||
.ant-menu-submenu-title {
|
||||
.ant-menu-submenu-arrow {
|
||||
&::before, &::after {
|
||||
background: @white;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
color: @white;
|
||||
.ant-menu-submenu-arrow {
|
||||
&::before, &::after {
|
||||
background: @white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-menu-submenu > .ant-menu {
|
||||
background-color: @primary-color-light;
|
||||
}
|
||||
.ant-menu-item > a:hover {
|
||||
color: @white;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-menu-horizontal {
|
||||
> .ant-menu-item-selected {
|
||||
color: @white;
|
||||
}
|
||||
}
|
||||
|
||||
.sider-custom {
|
||||
.ant-menu-submenu-title {
|
||||
color: @white;
|
||||
}
|
||||
}
|
||||
8
src/style/antd/reset.less
Normal file
8
src/style/antd/reset.less
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* File: reset.less
|
||||
* Desc: 样式重写
|
||||
* File Created: 2020-04-12 23:08:16
|
||||
* Author: chenghao
|
||||
* ------
|
||||
* Copyright 2020 - present, karakal
|
||||
*/
|
||||
7
src/style/antd/utils.less
Normal file
7
src/style/antd/utils.less
Normal file
@@ -0,0 +1,7 @@
|
||||
[class*=btn] {
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.bg--primary {
|
||||
background: @primary-color;
|
||||
}
|
||||
18
src/style/antd/variables.less
Normal file
18
src/style/antd/variables.less
Normal file
@@ -0,0 +1,18 @@
|
||||
@import "../../../node_modules/antd/lib/style/themes/default.less";
|
||||
// 基础颜色
|
||||
@white: #ffffff;
|
||||
@primary-color: #313653;
|
||||
@primary-color-light: fade(lighten(@primary-color, 5%), 15%);
|
||||
// 顶部背景色
|
||||
@layout-header-background:@primary-color;
|
||||
// 左边菜单light颜色
|
||||
@layout-sider-background-light: #f9f9f9;
|
||||
// 字体颜色
|
||||
@text-color: #000000;
|
||||
@table-selected-row-bg: #fbfbfb;
|
||||
@primary-2: @primary-color-light;
|
||||
// 基础圆角
|
||||
@border-radius-base: 2px;
|
||||
// 输入框后缀背景色
|
||||
@input-addon-bg: @primary-color;
|
||||
|
||||
23
src/style/app.less
Normal file
23
src/style/app.less
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* File: app.less
|
||||
* Desc: 描述
|
||||
* File Created: 2020-07-26 18:27:37
|
||||
* Author: yezi
|
||||
* ------
|
||||
* Copyright 2020 - present, yezi
|
||||
*/
|
||||
@prefix: app;
|
||||
|
||||
.@{prefix} {
|
||||
&_layout {
|
||||
flex-direction: column;
|
||||
&_content {
|
||||
margin: 0 16px;
|
||||
overflow: initial;
|
||||
flex: 1 1 0;
|
||||
}
|
||||
&_foot {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
122
src/style/banner.less
Normal file
122
src/style/banner.less
Normal file
@@ -0,0 +1,122 @@
|
||||
.banner-user {
|
||||
height: 200px;
|
||||
}
|
||||
.banner-user-elem {
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
.banner-user-title {
|
||||
font-size: 32px;
|
||||
top: 40%;
|
||||
}
|
||||
.banner-user-text {
|
||||
top: 40%;
|
||||
}
|
||||
}
|
||||
.banner-anim-elem {
|
||||
.bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-arrow-thumb{
|
||||
height: 220px;
|
||||
.user-arrow {
|
||||
top: 50%;
|
||||
margin-top: -40px;
|
||||
.img-wrapper {
|
||||
width: 120px;
|
||||
height: 80px;
|
||||
float: left;
|
||||
position: relative;
|
||||
li {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
.arrow {
|
||||
width: 20px;
|
||||
height: 80px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
position: relative;
|
||||
&:before, &:after {
|
||||
width: 2px;
|
||||
height: 15px;
|
||||
background: #fff;
|
||||
display: block;
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
&.next {
|
||||
right: -120px;
|
||||
.arrow {
|
||||
float: left;
|
||||
&:before {
|
||||
-webkit-transform: rotate(-40deg);
|
||||
transform: rotate(-40deg);
|
||||
top: 28px;
|
||||
left: 10px;
|
||||
}
|
||||
&:after {
|
||||
-webkit-transform: rotate(40deg);
|
||||
transform: rotate(40deg);
|
||||
bottom: 27px;
|
||||
left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.prev {
|
||||
left: -120px;
|
||||
.arrow {
|
||||
float: right;
|
||||
&:before {
|
||||
-webkit-transform: rotate(40deg);
|
||||
transform: rotate(40deg);
|
||||
top: 28px;
|
||||
left: 8px;
|
||||
}
|
||||
&:after {
|
||||
-webkit-transform: rotate(-40deg);
|
||||
transform: rotate(-40deg);
|
||||
bottom: 27px;
|
||||
left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.user-thumb {
|
||||
overflow: hidden;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
height: 40px;
|
||||
> span {
|
||||
width: 50px;
|
||||
height: 30px;
|
||||
margin: 5px;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.15);
|
||||
-webkit-transition: background .3s;
|
||||
transition: background .3s;
|
||||
background: transparent;
|
||||
&.active {
|
||||
background: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
i {
|
||||
display: block;
|
||||
width: 46px;
|
||||
height: 26px;
|
||||
margin: 2px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
src/style/button.less
Normal file
3
src/style/button.less
Normal file
@@ -0,0 +1,3 @@
|
||||
.ant-btn+.ant-btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
44
src/style/card.less
Normal file
44
src/style/card.less
Normal file
@@ -0,0 +1,44 @@
|
||||
.react-draggable, .cursor-move{
|
||||
cursor: move;
|
||||
strong {
|
||||
background: #ddd;
|
||||
border: 1px solid #999;
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
padding: 3px 5px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.no-cursor {
|
||||
cursor: auto;
|
||||
}
|
||||
.card-tool {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
top: 24px;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
.list-group-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-bottom: -1px;
|
||||
padding: 12px 16px;
|
||||
background: transparent;
|
||||
border: 1px solid #ddd;
|
||||
border-color: rgba(120, 130, 140, 0.065);
|
||||
border-width: 1px 0;
|
||||
&:first-child {
|
||||
border-top-width: 0;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.no-padding {
|
||||
.ant-card-body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
BIN
src/style/font/y6oxFxU60dYw9khW6q8jGw.woff2
Normal file
BIN
src/style/font/y6oxFxU60dYw9khW6q8jGw.woff2
Normal file
Binary file not shown.
71
src/style/global.less
Normal file
71
src/style/global.less
Normal file
@@ -0,0 +1,71 @@
|
||||
small {
|
||||
opacity: .6;
|
||||
}
|
||||
.text-muted{
|
||||
opacity: .6;
|
||||
}
|
||||
.clear{
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.y-center{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.block{
|
||||
display: block;
|
||||
}
|
||||
.inline {
|
||||
display: inline;
|
||||
}
|
||||
.none{
|
||||
display: none;
|
||||
}
|
||||
.b-white {
|
||||
border-color: #ffffff;
|
||||
}
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.w-auto {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.h-auto {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.h-v {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.h-v-5 {
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
|
||||
.pull-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.w-40 {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
25
src/style/icons.less
Normal file
25
src/style/icons.less
Normal file
@@ -0,0 +1,25 @@
|
||||
ul.icons-list{
|
||||
list-style: none;
|
||||
overflow: hidden;
|
||||
li{
|
||||
float: left;
|
||||
width: 10%;
|
||||
text-align: center;
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
height: 100px;
|
||||
transition: all .3s;
|
||||
background-color: #fff;
|
||||
&:hover{
|
||||
background-color: #cccccc;
|
||||
color: #fff;
|
||||
}
|
||||
i{
|
||||
margin: 16px 0 10px;
|
||||
}
|
||||
span{
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/style/img.less
Normal file
10
src/style/img.less
Normal file
@@ -0,0 +1,10 @@
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.img-responsive{
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.img-circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
BIN
src/style/imgs/404.png
Normal file
BIN
src/style/imgs/404.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
BIN
src/style/imgs/b1.jpg
Normal file
BIN
src/style/imgs/b1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
src/style/imgs/beauty.jpg
Normal file
BIN
src/style/imgs/beauty.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 157 KiB |
BIN
src/style/imgs/installer.png
Normal file
BIN
src/style/imgs/installer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/style/imgs/logo.png
Normal file
BIN
src/style/imgs/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user