Merge pull request #258 from zuiidea/develop

add:nprogress,dva-loading
This commit is contained in:
zuiidea
2017-05-08 15:34:15 +08:00
committed by GitHub
15 changed files with 170 additions and 125 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ import Menus from './Menu'
const SubMenu = Menu.SubMenu
function Header ({ user, logout, switchSider, siderFold, isNavbar, menuPopoverVisible, location, switchMenuPopover, navOpenKeys, changeOpenKeys, menu }) {
const Header = ({ user, logout, switchSider, siderFold, isNavbar, menuPopoverVisible, location, switchMenuPopover, navOpenKeys, changeOpenKeys, menu }) => {
let handleClickMenu = e => e.key === 'logout' && logout()
const menusProps = {
menu,
+1
View File
@@ -17,6 +17,7 @@
.rightWarpper {
display: flex;
padding-right: 16px;
}
.button {
-1
View File
@@ -69,7 +69,6 @@
height: 100vh;
transition: @transition-ease-out;
box-shadow: @shadow-1;
z-index: 520;
color: #999;
&.light {
+1 -1
View File
@@ -5,7 +5,7 @@ import styles from './Layout.less'
import { config } from '../../utils'
import Menus from './Menu'
function Sider ({ siderFold, darkTheme, location, changeTheme, navOpenKeys, changeOpenKeys, menu }) {
const Sider = ({ siderFold, darkTheme, location, changeTheme, navOpenKeys, changeOpenKeys, menu }) => {
const menusProps = {
menu,
siderFold,
+3 -1
View File
@@ -7,7 +7,9 @@ import { message } from 'antd'
// 1. Initialize
const app = dva({
...createLoading(),
...createLoading({
effects: true,
}),
history: browserHistory,
onError (error) {
message.error(error.message)
+34 -53
View File
@@ -8,7 +8,6 @@ export default {
namespace: 'app',
state: {
user: {},
loginButtonLoading: false,
menuPopoverVisible: false,
siderFold: localStorage.getItem(`${prefix}siderFold`) === 'true',
darkTheme: localStorage.getItem(`${prefix}darkTheme`) === 'true',
@@ -16,21 +15,28 @@ export default {
navOpenKeys: JSON.parse(localStorage.getItem(`${prefix}navOpenKeys`)) || [],
},
subscriptions: {
setup ({ dispatch }) {
dispatch({ type: 'queryUser' })
dispatch({ type: 'query' })
let tid
window.onresize = () => {
dispatch({ type: 'changeNavbar' })
clearTimeout(tid)
tid = setTimeout(() => {
dispatch({ type: 'changeNavbar' })
}, 300)
}
},
},
effects: {
*queryUser ({
*query ({
payload,
}, { call, put }) {
const data = yield call(query, parse(payload))
if (data.success && data.user) {
yield put({
type: 'queryUserSuccess',
type: 'querySuccess',
payload: data.user,
})
if (location.pathname === '/login') {
@@ -46,6 +52,7 @@ export default {
}
}
},
*logout ({
payload,
}, { call, put }) {
@@ -56,82 +63,56 @@ export default {
throw (data)
}
},
*switchSider ({
payload,
}, { put }) {
yield put({
type: 'handleSwitchSider',
})
},
*changeTheme ({
payload,
}, { put }) {
yield put({
type: 'handleChangeTheme',
})
},
*changeNavbar ({
payload,
}, { put }) {
if (document.body.clientWidth < 769) {
yield put({ type: 'showNavbar' })
} else {
yield put({ type: 'hideNavbar' })
}, { put, select }) {
const { app } = yield(select(_ => _))
const isNavbar = document.body.clientWidth < 769
if (isNavbar !== app.isNavbar) {
yield put({ type: 'handleNavbar', payload: isNavbar })
}
},
*switchMenuPopver ({
payload,
}, { put }) {
yield put({
type: 'handleSwitchMenuPopver',
})
},
},
reducers: {
queryUserSuccess (state, { payload: user }) {
querySuccess (state, { payload: user }) {
return {
...state,
user,
}
},
showLoginButtonLoading (state) {
return {
...state,
loginButtonLoading: true,
}
},
handleSwitchSider (state) {
switchSider (state) {
localStorage.setItem(`${prefix}siderFold`, !state.siderFold)
return {
...state,
siderFold: !state.siderFold,
}
},
handleChangeTheme (state) {
switchTheme (state) {
localStorage.setItem(`${prefix}darkTheme`, !state.darkTheme)
return {
...state,
darkTheme: !state.darkTheme,
}
},
showNavbar (state) {
return {
...state,
isNavbar: true,
}
},
hideNavbar (state) {
return {
...state,
isNavbar: false,
}
},
handleSwitchMenuPopver (state) {
switchMenuPopver (state) {
return {
...state,
menuPopoverVisible: !state.menuPopoverVisible,
}
},
handleNavbar (state, { payload }) {
return {
...state,
isNavbar: payload,
}
},
handleNavOpenKeys (state, { payload: navOpenKeys }) {
return {
...state,
+17 -20
View File
@@ -1,6 +1,5 @@
import { create, remove, update } from '../services/user'
import { query } from '../services/users'
import { routerRedux } from 'dva/router'
import { parse } from 'qs'
export default {
@@ -36,13 +35,9 @@ export default {
},
effects: {
*requery ({ payload }, { put }) {
yield put(routerRedux.push({
pathname: location.pathname,
query: parse(location.search.substr(1)),
}))
},
*query ({ payload }, { call, put }) {
payload = parse(location.search.substr(1))
const data = yield call(query, payload)
if (data) {
yield put({
@@ -58,44 +53,42 @@ export default {
})
}
},
*'delete' ({ payload }, { call, put }) {
const data = yield call(remove, { id: payload })
if (data.success) {
yield put({ type: 'requery' })
yield put({ type: 'query' })
} else {
throw data
}
},
*create ({ payload }, { call, put }) {
yield put({ type: 'hideModal' })
const data = yield call(create, payload)
if (data.success) {
yield put({ type: 'requery' })
yield put({ type: 'hideModal' })
yield put({ type: 'query' })
} else {
throw data
}
},
*update ({ payload }, { select, call, put }) {
yield put({ type: 'hideModal' })
const id = yield select(({ user }) => user.currentItem.id)
const newUser = { ...payload, id }
const data = yield call(update, newUser)
if (data.success) {
yield put({ type: 'requery' })
yield put({ type: 'hideModal' })
yield put({ type: 'query' })
} else {
throw data
}
},
*switchIsMotion ({
payload,
}, { put }) {
yield put({
type: 'handleSwitchIsMotion',
})
},
},
reducers: {
querySuccess (state, action) {
const { list, pagination } = action.payload
return { ...state,
@@ -105,16 +98,20 @@ export default {
...pagination,
} }
},
showModal (state, action) {
return { ...state, ...action.payload, modalVisible: true }
},
hideModal (state) {
return { ...state, modalVisible: false }
},
handleSwitchIsMotion (state) {
switchIsMotion (state) {
localStorage.setItem('antdAdminUserIsMotion', !state.isMotion)
return { ...state, isMotion: !state.isMotion }
},
},
}
View File
+8 -2
View File
@@ -5,13 +5,18 @@ import { Layout } from '../components'
import { classnames, config, menu } from '../utils'
import { Helmet } from 'react-helmet'
import '../themes/index.less'
import './app.less'
import NProgress from 'nprogress'
const { prefix } = config
const { Header, Bread, Footer, Sider, styles } = Layout
const App = ({ children, location, dispatch, app }) => {
const App = ({ children, location, dispatch, app, loading }) => {
const { user, siderFold, darkTheme, isNavbar, menuPopoverVisible, navOpenKeys } = app
NProgress.start()
!loading.global && NProgress.done()
const headerProps = {
menu,
user,
@@ -41,7 +46,7 @@ const App = ({ children, location, dispatch, app }) => {
location,
navOpenKeys,
changeTheme () {
dispatch({ type: 'app/changeTheme' })
dispatch({ type: 'app/switchTheme' })
},
changeOpenKeys (openKeys) {
localStorage.setItem(`${prefix}navOpenKeys`, JSON.stringify(openKeys))
@@ -89,6 +94,7 @@ App.propTypes = {
location: PropTypes.object,
dispatch: PropTypes.func,
app: PropTypes.object,
loading: PropTypes.object,
}
export default connect(({ app, loading }) => ({ app, loading }))(App)
+72
View File
@@ -0,0 +1,72 @@
@import "../themes/vars.less";
:global {
#nprogress {
pointer-events: none;
.bar {
background: @primary-color;
position: fixed;
z-index: 1024;
top: 0;
left: 0;
right: 0;
width: 100%;
height: 2px;
}
.peg {
display: block;
position: absolute;
right: 0;
width: 100px;
height: 100%;
box-shadow: 0 0 10px @primary-color,0 0 5px @primary-color;
opacity: 1.0;
transform: rotate(3deg) translate(0px,-4px);
}
.spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
right: 15px;
}
.spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: @primary-color;
border-left-color: @primary-color;
border-radius: 50%;
:local {
animation: nprogress-spinner 400ms linear infinite;
}
}
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
#nprogress {
.bar,
.spinner {
position: absolute;
}
}
}
}
@keyframes nprogress-spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@@ -21,7 +21,7 @@ const TwoColProps = {
xl: 96,
}
const UserFilter = ({
const Filter = ({
onAdd,
isMotion,
switchIsMotion,
@@ -44,7 +44,6 @@ const UserFilter = ({
const handleSubmit = () => {
let fields = getFieldsValue()
fields = handleFields(fields)
console.log(fields)
onFilterChange(fields)
}
@@ -117,7 +116,7 @@ const UserFilter = ({
)
}
UserFilter.propTypes = {
Filter.propTypes = {
onAdd: PropTypes.func,
isMotion: PropTypes.bool,
switchIsMotion: PropTypes.func,
@@ -126,4 +125,4 @@ UserFilter.propTypes = {
onFilterChange: PropTypes.func,
}
export default Form.create()(UserFilter)
export default Form.create()(Filter)
@@ -1,7 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Table, Modal } from 'antd'
import styles from './UserList.less'
import styles from './List.less'
import classnames from 'classnames'
import AnimTableBody from '../../components/DataTable/AnimTableBody'
import { DropOption } from '../../components'
@@ -9,7 +9,7 @@ import { Link } from 'dva/router'
const confirm = Modal.confirm
function list ({ loading, dataSource, pagination, onPageChange, onDeleteItem, onEditItem, isMotion, location }) {
const List = ({ onDeleteItem, onEditItem, isMotion, location, ...tableProps }) => {
const handleMenuClick = (record, e) => {
if (e.key === '1') {
onEditItem(record)
@@ -72,14 +72,14 @@ function list ({ loading, dataSource, pagination, onPageChange, onDeleteItem, on
key: 'operation',
width: 100,
render: (text, record) => {
return <DropOption onMenuClick={e => handleMenuClick(record, e)} menuOptions={[{ key: '1', name: '编辑' }, { key: '2', name: '删除' }]} />
return <DropOption onMenuClick={e => handleMenuClick(record, e)} menuOptions={[{ key: '1', name: 'Update' }, { key: '2', name: 'Delete' }]} />
},
},
]
const getBodyWrapperProps = {
page: location.query.page,
current: pagination.current,
current: tableProps.pagination.current,
}
const getBodyWrapper = body => { return isMotion ? <AnimTableBody {...getBodyWrapperProps} body={body} /> : body }
@@ -87,14 +87,11 @@ function list ({ loading, dataSource, pagination, onPageChange, onDeleteItem, on
return (
<div>
<Table
{...tableProps}
className={classnames({ [styles.table]: true, [styles.motion]: isMotion })}
bordered
scroll={{ x: 1200 }}
columns={columns}
dataSource={dataSource}
loading={loading}
onChange={onPageChange}
pagination={pagination}
simple
rowKey={record => record.id}
getBodyWrapper={getBodyWrapper}
@@ -103,15 +100,11 @@ function list ({ loading, dataSource, pagination, onPageChange, onDeleteItem, on
)
}
list.propTypes = {
loading: PropTypes.bool,
dataSource: PropTypes.array,
pagination: PropTypes.object,
onPageChange: PropTypes.func,
List.propTypes = {
onDeleteItem: PropTypes.func,
onEditItem: PropTypes.func,
isMotion: PropTypes.bool,
location: PropTypes.object,
}
export default list
export default List
@@ -15,18 +15,16 @@ const formItemLayout = {
}
const modal = ({
visible,
type,
item = {},
onOk,
onCancel,
form: {
getFieldDecorator,
validateFields,
getFieldsValue,
},
...modalProps
}) => {
function handleOk () {
const handleOk = () => {
validateFields((errors) => {
if (errors) {
return
@@ -41,11 +39,8 @@ const modal = ({
}
const modalOpts = {
title: `${type === 'create' ? 'Create User' : 'Update User'}`,
visible,
...modalProps,
onOk: handleOk,
onCancel,
wrapClassName: 'vertical-center-modal',
}
return (
@@ -105,17 +100,19 @@ const modal = ({
{
required: true,
pattern: /^1[34578]\d{9}$/,
message: 'The input is not valid phone!',
},
],
})(<Input />)}
</FormItem>
<FormItem label="Email" hasFeedback {...formItemLayout}>
<FormItem label="E-mail" hasFeedback {...formItemLayout}>
{getFieldDecorator('email', {
initialValue: item.email,
rules: [
{
required: true,
pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
message: 'The input is not valid E-mail!',
},
],
})(<Input />)}
@@ -142,10 +139,8 @@ const modal = ({
modal.propTypes = {
form: PropTypes.object.isRequired,
visible: PropTypes.bool,
type: PropTypes.string,
item: PropTypes.object,
onCancel: PropTypes.func,
onOk: PropTypes.func,
}
+17 -17
View File
@@ -2,18 +2,21 @@ import React from 'react'
import PropTypes from 'prop-types'
import { routerRedux } from 'dva/router'
import { connect } from 'dva'
import UserList from './UserList'
import UserFilter from './UserFilter'
import UserModal from './UserModal'
import List from './List'
import Filter from './Filter'
import Modal from './Modal'
const User = ({ location, dispatch, user, loading }) => {
const { list, pagination, currentItem, modalVisible, modalType, isMotion } = user
const { pageSize } = pagination
const userModalProps = {
const modalProps = {
item: modalType === 'create' ? {} : currentItem,
type: modalType,
visible: modalVisible,
maskClosable: false,
confirmLoading: loading.effects['user/update'],
title: `${modalType === 'create' ? 'Create User' : 'Update User'}`,
wrapClassName: 'vertical-center-modal',
onOk (data) {
dispatch({
type: `user/${modalType}`,
@@ -27,13 +30,13 @@ const User = ({ location, dispatch, user, loading }) => {
},
}
const userListProps = {
const listProps = {
dataSource: list,
loading,
loading: loading.effects['user/query'],
pagination,
location,
isMotion,
onPageChange (page) {
onChange (page) {
const { query, pathname } = location
dispatch(routerRedux.push({
pathname,
@@ -61,7 +64,7 @@ const User = ({ location, dispatch, user, loading }) => {
},
}
const userFilterProps = {
const filterProps = {
isMotion,
filter: {
...location.query,
@@ -100,14 +103,11 @@ const User = ({ location, dispatch, user, loading }) => {
},
}
const UserModalGen = () =>
<UserModal {...userModalProps} />
return (
<div className="content-inner">
<UserFilter {...userFilterProps} />
<UserList {...userListProps} />
<UserModalGen />
<Filter {...filterProps} />
<List {...listProps} />
{modalVisible && <Modal {...modalProps} />}
</div>
)
}
@@ -116,7 +116,7 @@ User.propTypes = {
user: PropTypes.object,
location: PropTypes.object,
dispatch: PropTypes.func,
loading: PropTypes.bool,
loading: PropTypes.object,
}
export default connect(({ user, loading }) => ({ user, loading: loading.models.user }))(User)
export default connect(({ user, loading }) => ({ user, loading }))(User)