diff --git a/src/components/Layout/Header.js b/src/components/Layout/Header.js index a6c2549..b9f4686 100644 --- a/src/components/Layout/Header.js +++ b/src/components/Layout/Header.js @@ -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, diff --git a/src/components/Layout/Header.less b/src/components/Layout/Header.less index eb108ec..5e5f7f6 100644 --- a/src/components/Layout/Header.less +++ b/src/components/Layout/Header.less @@ -17,6 +17,7 @@ .rightWarpper { display: flex; + padding-right: 16px; } .button { diff --git a/src/components/Layout/Layout.less b/src/components/Layout/Layout.less index 6ab9521..750124b 100644 --- a/src/components/Layout/Layout.less +++ b/src/components/Layout/Layout.less @@ -69,7 +69,6 @@ height: 100vh; transition: @transition-ease-out; box-shadow: @shadow-1; - z-index: 520; color: #999; &.light { diff --git a/src/components/Layout/Sider.js b/src/components/Layout/Sider.js index 96e5b33..d60834d 100644 --- a/src/components/Layout/Sider.js +++ b/src/components/Layout/Sider.js @@ -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, diff --git a/src/index.js b/src/index.js index 7cd2a9f..570b080 100644 --- a/src/index.js +++ b/src/index.js @@ -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) diff --git a/src/models/app.js b/src/models/app.js index 2958ae0..3d9c1c3 100644 --- a/src/models/app.js +++ b/src/models/app.js @@ -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, diff --git a/src/models/user.js b/src/models/user.js index 3ca2d4a..9c9cdda 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -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 } }, + }, } diff --git a/src/models/user/index.js b/src/models/user/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/routes/app.js b/src/routes/app.js index a91fa67..f22eeb8 100644 --- a/src/routes/app.js +++ b/src/routes/app.js @@ -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) diff --git a/src/routes/app.less b/src/routes/app.less new file mode 100644 index 0000000..2e897fd --- /dev/null +++ b/src/routes/app.less @@ -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); + } +} diff --git a/src/routes/user/UserFilter.js b/src/routes/user/Filter.js similarity index 96% rename from src/routes/user/UserFilter.js rename to src/routes/user/Filter.js index 80e6a71..2658b46 100644 --- a/src/routes/user/UserFilter.js +++ b/src/routes/user/Filter.js @@ -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) diff --git a/src/routes/user/UserList.js b/src/routes/user/List.js similarity index 81% rename from src/routes/user/UserList.js rename to src/routes/user/List.js index ec8c712..6e58322 100644 --- a/src/routes/user/UserList.js +++ b/src/routes/user/List.js @@ -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 handleMenuClick(record, e)} menuOptions={[{ key: '1', name: '编辑' }, { key: '2', name: '删除' }]} /> + return 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 ? : body } @@ -87,14 +87,11 @@ function list ({ loading, dataSource, pagination, onPageChange, onDeleteItem, on return (
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 diff --git a/src/routes/user/UserList.less b/src/routes/user/List.less similarity index 100% rename from src/routes/user/UserList.less rename to src/routes/user/List.less diff --git a/src/routes/user/UserModal.js b/src/routes/user/Modal.js similarity index 91% rename from src/routes/user/UserModal.js rename to src/routes/user/Modal.js index 4c41670..b5b219f 100644 --- a/src/routes/user/UserModal.js +++ b/src/routes/user/Modal.js @@ -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!', }, ], })()} - + {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!', }, ], })()} @@ -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, } diff --git a/src/routes/user/index.js b/src/routes/user/index.js index 95363e5..ceced18 100644 --- a/src/routes/user/index.js +++ b/src/routes/user/index.js @@ -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 = () => - - return (
- - - + + + {modalVisible && }
) } @@ -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)