mirror of
https://github.com/zuiidea/antd-admin
synced 2026-10-29 20:44:05 +00:00
@@ -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,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
.rightWarpper {
|
||||
display: flex;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.button {
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
height: 100vh;
|
||||
transition: @transition-ease-out;
|
||||
box-shadow: @shadow-1;
|
||||
z-index: 520;
|
||||
color: #999;
|
||||
|
||||
&.light {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 }
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
+8
-2
@@ -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)
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user