diff --git a/src/components/Breadcrumb/index.js b/src/components/Layout/Breadcrumb/index.js
similarity index 100%
rename from src/components/Breadcrumb/index.js
rename to src/components/Layout/Breadcrumb/index.js
diff --git a/src/components/Footer/index.js b/src/components/Layout/Footer/index.js
similarity index 100%
rename from src/components/Footer/index.js
rename to src/components/Layout/Footer/index.js
diff --git a/src/components/Header/index.js b/src/components/Layout/Header/index.js
similarity index 100%
rename from src/components/Header/index.js
rename to src/components/Layout/Header/index.js
diff --git a/src/components/Header/index.less b/src/components/Layout/Header/index.less
similarity index 100%
rename from src/components/Header/index.less
rename to src/components/Layout/Header/index.less
diff --git a/src/components/Sidebar/index.js b/src/components/Layout/Sidebar/index.js
similarity index 100%
rename from src/components/Sidebar/index.js
rename to src/components/Layout/Sidebar/index.js
diff --git a/src/components/Users/UserList.jsx b/src/components/Users/UserList.jsx
new file mode 100644
index 0000000..3f8a68f
--- /dev/null
+++ b/src/components/Users/UserList.jsx
@@ -0,0 +1,67 @@
+import React, { PropTypes } from 'react';
+import { Table, Popconfirm, Pagination } from 'antd';
+
+function UserList({
+ total, current, loading, dataSource,
+ onPageChange,
+ onDeleteItem,
+ onEditItem,
+ }) {
+ const columns = [{
+ title: '姓名',
+ dataIndex: 'name',
+ key: 'name',
+ render: (text) => {text},
+ }, {
+ title: '年龄',
+ dataIndex: 'age',
+ key: 'age',
+ }, {
+ title: '住址',
+ dataIndex: 'address',
+ key: 'address',
+ }, {
+ title: '操作',
+ key: 'operation',
+ render: (text, record) => (
+
+ onEditItem(record)}>编辑
+
+ onDeleteItem(record.id)}>
+ 删除
+
+
+ ),
+ }];
+
+ return (
+
+
record.id}
+ pagination={false}
+ />
+
+
+ );
+}
+
+UserList.propTypes = {
+ onPageChange: PropTypes.func,
+ onDeleteItem: PropTypes.func,
+ onEditItem: PropTypes.func,
+ dataSource: PropTypes.array,
+ loading: PropTypes.any,
+ total: PropTypes.any,
+ current: PropTypes.any,
+};
+
+export default UserList;
diff --git a/src/components/Users/UserModal.jsx b/src/components/Users/UserModal.jsx
new file mode 100644
index 0000000..116126e
--- /dev/null
+++ b/src/components/Users/UserModal.jsx
@@ -0,0 +1,109 @@
+import React, { PropTypes } from 'react'
+import { Form, Input, Modal } from 'antd'
+const FormItem = Form.Item
+
+const formItemLayout = {
+ labelCol: {
+ span: 6,
+ },
+ wrapperCol: {
+ span: 14,
+ },
+}
+
+const UserModal = ({
+ visible,
+ item = {},
+ onOk,
+ onCancel,
+ form: {
+ getFieldDecorator,
+ validateFields,
+ getFieldsValue,
+ },
+ }) => {
+ function handleOk() {
+ validateFields((errors) => {
+ if (errors) {
+ return
+ }
+ const data = { ...getFieldsValue(), key: item.key }
+ onOk(data)
+ })
+ }
+
+ function checkNumber(rule, value, callback) {
+ if (!/^[\d]{1,2}$/.test(value)) {
+ callback(new Error('年龄不合法'))
+ } else {
+ callback()
+ }
+ }
+
+ const modalOpts = {
+ title: '修改用户',
+ visible,
+ onOk: handleOk,
+ onCancel,
+ }
+
+ return (
+
+
+
+ )
+}
+
+UserModal.propTypes = {
+ visible: PropTypes.any,
+ form: PropTypes.object,
+ item: PropTypes.object,
+ onOk: PropTypes.func,
+ onCancel: PropTypes.func,
+}
+
+export default Form.create()(UserModal)
diff --git a/src/components/Users/UserSearch.jsx b/src/components/Users/UserSearch.jsx
new file mode 100644
index 0000000..503ac73
--- /dev/null
+++ b/src/components/Users/UserSearch.jsx
@@ -0,0 +1,65 @@
+import React, {PropTypes} from 'react'
+import {Form, Input, Button, Select} from 'antd'
+import styles from './UserSearch.less'
+
+const UserSearch = ({
+ field,
+ keyword,
+ onSearch,
+ onAdd,
+ form: {
+ getFieldDecorator,
+ validateFields,
+ getFieldsValue
+ }
+}) => {
+ function handleSubmit(e) {
+ e.preventDefault()
+ validateFields((errors) => {
+ if (!!errors) {
+ return
+ }
+ onSearch(getFieldsValue())
+ })
+ }
+
+ return (
+
+
+
+ {getFieldDecorator('field', {
+ initialValue: field || 'name'
+ })(
+
+ )}
+
+
+ {getFieldDecorator('keyword', {
+ initialValue: keyword || ''
+ })()}
+
+
+
+
+
+
+
+
+ )
+}
+
+UserSearch.propTypes = {
+ form: PropTypes.object.isRequired,
+ onSearch: PropTypes.func,
+ onAdd: PropTypes.func,
+ field: PropTypes.string,
+ keyword: PropTypes.string
+}
+
+export default Form.create()(UserSearch)
diff --git a/src/components/Users/UserSearch.less b/src/components/Users/UserSearch.less
new file mode 100644
index 0000000..14d06b2
--- /dev/null
+++ b/src/components/Users/UserSearch.less
@@ -0,0 +1,8 @@
+.normal {
+ display: flex;
+ margin-bottom: 20px;
+}
+
+.search {
+ flex: 1;
+}
diff --git a/src/models/example.js b/src/models/example.js
deleted file mode 100644
index c54bf1c..0000000
--- a/src/models/example.js
+++ /dev/null
@@ -1,24 +0,0 @@
-
-export default {
-
- namespace: 'example',
-
- state: {},
-
- subscriptions: {
- setup({ dispatch, history }) {
- },
- },
-
- effects: {
- *fetchRemote({ payload }, { call, put }) {
- },
- },
-
- reducers: {
- fetch(state, action) {
- return { ...state, ...action.payload };
- },
- },
-
-}
diff --git a/src/models/users.js b/src/models/users.js
new file mode 100644
index 0000000..f8fd859
--- /dev/null
+++ b/src/models/users.js
@@ -0,0 +1,144 @@
+import {create, remove, update, query} from '../services/users'
+import {parse} from 'qs'
+
+export default {
+
+ namespace : 'users',
+
+ state : {
+ list: [],
+ loading: false,
+ total: null,
+ current: 1,
+ currentItem: {},
+ modalVisible: false,
+ modalType: 'create'
+ },
+
+ subscriptions : {
+ setup({dispatch, history}) {
+ history.listen(location => {
+ if (location.pathname === '/users') {
+ dispatch({type: 'query', payload: location.query})
+ }
+ })
+ }
+ },
+
+ effects : {
+ *query({
+ payload
+ }, {call, put}) {
+ yield put({type: 'showLoading'})
+ const {data} = yield call(query, parse(payload))
+ if (data) {
+ yield put({
+ type: 'querySuccess',
+ payload: {
+ list: data.data,
+ total: data.page.total,
+ current: data.page.current
+ }
+ })
+ }
+ },
+ * 'delete' ({
+ payload
+ }, {call, put}) {
+ yield put({type: 'showLoading'})
+ const {data} = yield call(remove, {id: payload})if (data && data.success) {
+ yield put({type: 'deleteSuccess', payload})
+ }
+ },
+ *create({
+ payload
+ }, {call, put}) {
+ yield put({type: 'hideModal'})
+ yield put({type: 'showLoading'})
+ const {data} = yield call(create, payload)
+ if (data && data.success) {
+ yield put({type: 'createSuccess', payload})
+ }
+ },
+ *update({
+ payload
+ }, {select, call, put}) {
+ yield put({type: 'hideModal'})
+ yield put({type: 'showLoading'})
+ const id = yield select(({users}) => users.currentItem.id)
+ const newUser = {
+ ...payload,
+ id
+ }
+ const {data} = yield call(update, newUser)
+ if (data && data.success) {
+ yield put({type: 'updateSuccess', payload: newUser})
+ }
+ }
+ },
+
+ reducers : {
+ showLoading(state) {
+ return {
+ ...state,
+ loading: true
+ }
+ },
+ createSuccess(state, action) {
+ const newUser = action.payload
+ return {
+ ...state,
+ list: [
+ newUser, ...state.list
+ ],
+ loading: false
+ }
+ },
+ deleteSuccess(state, action) {
+ const id = action.payload
+ const newList = state.list.filter(user => user.id !== id)
+ return {
+ ...state,
+ list: newList,
+ loading: false
+ }
+ },
+ updateSuccess(state, action) {
+ const updateUser = action.payload
+ const newList = state.list.map(user => {
+ if (user.id === updateUser.id) {
+ return {
+ ...user,
+ ...updateUser
+ }
+ }
+ return user
+ })
+ return {
+ ...state,
+ list: newList,
+ loading: false
+ }
+ },
+ querySuccess(state, action) {
+ return {
+ ...state,
+ ...action.payload,
+ loading: false
+ }
+ },
+ showModal(state, action) {
+ return {
+ ...state,
+ ...action.payload,
+ modalVisible: true
+ }
+ },
+ hideModal(state) {
+ return {
+ ...state,
+ modalVisible: false
+ }
+ }
+ }
+}