diff --git a/mock/users.js b/mock/users.js
new file mode 100644
index 0000000..e168aa1
--- /dev/null
+++ b/mock/users.js
@@ -0,0 +1,130 @@
+'use strict';
+
+const qs = require('qs');
+const mockjs = require('mockjs');
+
+// 数据持久
+let tableListData = {};
+if (!global.tableListData) {
+ const data = mockjs.mock({
+ 'data|100': [{
+ 'id|+1': 1,
+ name: '@cname',
+ 'age|11-99': 1,
+ address: '@region'
+ }],
+ page: {
+ total: 100,
+ current: 1
+ }
+ });
+ tableListData = data;
+ global.tableListData = tableListData;
+} else {
+ tableListData = global.tableListData;
+}
+
+module.exports = {
+
+ 'GET /api/users' (req, res) {
+ const page = qs.parse(req.query);
+ const pageSize = page.pageSize || 10;
+ const currentPage = page.page || 1;
+
+ let data;
+ let newPage;
+
+ let newData = tableListData.data.concat();
+
+ if (page.field) {
+ const d = newData.filter(function (item) {
+ return item[page.field].indexOf(page.keyword) > -1;
+ });
+
+ data = d.slice((currentPage - 1) * pageSize, currentPage * pageSize);
+
+ newPage = {
+ current: currentPage * 1,
+ total: d.length
+ };
+ } else {
+ data = tableListData.data.slice((currentPage - 1) * pageSize, currentPage * pageSize);
+ tableListData.page.current = currentPage * 1;
+ newPage = {
+ current: tableListData.page.current,
+ total: tableListData.page.total
+ };
+ }
+
+
+ setTimeout(function () {
+ res.json({
+ success: true,
+ data,
+ page: newPage
+ });
+ }, 500);
+ },
+
+ 'POST /api/users' (req, res) {
+ setTimeout(function () {
+ const newData = qs.parse(req.body);
+
+ newData.id = tableListData.data.length + 1;
+ tableListData.data.unshift(newData);
+
+ tableListData.page.total = tableListData.data.length;
+ tableListData.page.current = 1;
+
+ global.tableListData = tableListData;
+ res.json({
+ success: true,
+ data: tableListData.data,
+ page: tableListData.page
+ });
+ }, 500);
+ },
+
+ 'DELETE /api/users' (req, res) {
+ setTimeout(function () {
+ const deleteItem = qs.parse(req.body);
+
+ tableListData.data = tableListData.data.filter(function (item) {
+ if (item.id == deleteItem.id) {
+ return false;
+ }
+ return true;
+ });
+
+ tableListData.page.total = tableListData.data.length;
+
+ global.tableListData = tableListData;
+ res.json({
+ success: true,
+ data: tableListData.data,
+ page: tableListData.page
+ });
+ }, 500);
+ },
+
+ 'PUT /api/users' (req, res) {
+ setTimeout(function () {
+ const editItem = qs.parse(req.body);
+
+ tableListData.data = tableListData.data.map(function (item) {
+ if (item.id == editItem.id) {
+ return editItem;
+ }
+ return item;
+ });
+
+ global.tableListData = tableListData;
+ res.json({
+ success: true,
+ data: tableListData.data,
+ page: tableListData.page
+ });
+ }, 500);
+ }
+
+};
diff --git a/src/index.js b/src/index.js
index 17aacde..6ccdb2b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,69 +1,15 @@
-import dva, { connect } from 'dva';
-import { Router, Route } from 'dva/router';
-import React from 'react';
-import styles from './index.less';
-import key from 'keymaster';
+import './index.html';
+import './index.less';
+import dva from 'dva';
+// 1. Initialize
const app = dva();
-app.model({
- namespace: 'count',
- state: {
- record: 0,
- current: 0,
- },
- reducers: {
- add(state) {
- const newCurrent = state.current + 1;
- return { ...state,
- record: newCurrent > state.record ? newCurrent : state.record,
- current: newCurrent,
- };
- },
- minus(state) {
- return { ...state, current: state.current - 1};
- },
- },
- effects: {
- *add(action, { call, put }) {
- yield call(delay, 1000);
- yield put({ type: 'count/minus' });
- },
- },
- subscriptions: {
- keyboardWatcher(dispatch) {
- key('⌘+up, ctrl+up', () => { dispatch({type:'count/add'}) });
- },
- },
-});
+// 2. Model
+app.model(require('./models/users'));
-const CountApp = ({count, dispatch}) => {
- return (
-
-
Highest Record: {count.record}
-
{count.current}
-
-
-
-
- );
-};
-
-function mapStateToProps(state) {
- return { count: state.count };
-}
-const HomePage = connect(mapStateToProps)(CountApp);
-
-app.router(({history}) =>
-
-
-
-);
+// 3. Router
+app.router(require('./router'));
+// 4. Start
app.start('#root');
-
-function delay(timeout){
- return new Promise(resolve => {
- setTimeout(resolve, timeout);
- });
-}
diff --git a/src/router.js b/src/router.js
index 13b526a..806b2c7 100644
--- a/src/router.js
+++ b/src/router.js
@@ -1,11 +1,16 @@
-import React, { PropTypes } from 'react';
-import { Router, Route, IndexRoute, Link } from 'dva/router';
-import IndexPage from './routes/IndexPage';
+import React from 'react';
+import { Router, Route } from 'dva/router';
+import HomePage from './routes/HomePage';
+import NotFound from './routes/NotFound';
+import Users from './routes/Users';
-export default function({ history }) {
+/* eslint react/prop-types:0 */
+export default function ({ history }) {
return (
-
+
+
+
);
-};
+}
diff --git a/src/routes/HomePage.jsx b/src/routes/HomePage.jsx
new file mode 100644
index 0000000..3931281
--- /dev/null
+++ b/src/routes/HomePage.jsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { Link } from 'dva/router';
+import styles from './HomePage.less';
+
+function HomePage() {
+ return (
+
+ );
+}
+
+HomePage.propTypes = {
+};
+
+export default HomePage;
diff --git a/src/routes/HomePage.less b/src/routes/HomePage.less
new file mode 100644
index 0000000..cc48bd6
--- /dev/null
+++ b/src/routes/HomePage.less
@@ -0,0 +1,8 @@
+
+.normal {
+ margin: 100px;
+}
+
+.list {
+ margin-top: 20px;
+}
diff --git a/src/routes/NotFound.jsx b/src/routes/NotFound.jsx
new file mode 100644
index 0000000..d136b79
--- /dev/null
+++ b/src/routes/NotFound.jsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { Button } from 'antd';
+import styles from './NotFound.less';
+
+const NotFound = () =>
+ ;
+
+export default NotFound;
diff --git a/src/routes/NotFound.less b/src/routes/NotFound.less
new file mode 100644
index 0000000..fd8d23b
--- /dev/null
+++ b/src/routes/NotFound.less
@@ -0,0 +1,25 @@
+.normal {
+ width: 100%;
+ height: 100%;
+ min-height: 100vh;
+ padding-top: 120px;
+}
+
+.container {
+ padding: 0;
+ margin: 0 auto;
+ width: 620px;
+ height: 300px;
+ text-align: center;
+}
+
+.title {
+ font-size: 80px;
+ color: #666;
+ margin-top: 20px;
+ margin-bottom: 10px;
+}
+
+.desc {
+ font-size: 14px;
+}
diff --git a/src/routes/Users.jsx b/src/routes/Users.jsx
new file mode 100644
index 0000000..59b9d16
--- /dev/null
+++ b/src/routes/Users.jsx
@@ -0,0 +1,109 @@
+import React, { PropTypes } from 'react';
+import { routerRedux } from 'dva/router';
+import { connect } from 'dva';
+import MainLayout from '../components/MainLayout/MainLayout';
+import styles from './Users.less';
+import UserList from '../components/Users/UserList';
+import UserSearch from '../components/Users/UserSearch';
+import UserModal from '../components/Users/UserModal';
+
+function Users({ location, dispatch, users }) {
+ const {
+ loading, list, total, current,
+ currentItem, modalVisible, modalType,
+ } = users;
+
+ const { field, keyword } = location.query;
+
+ const userModalProps = {
+ item: modalType === 'create' ? {} : currentItem,
+ type: modalType,
+ visible: modalVisible,
+ onOk(data) {
+ dispatch({
+ type: `users/${modalType}`,
+ payload: data,
+ });
+ },
+ onCancel() {
+ dispatch({
+ type: 'users/hideModal',
+ });
+ },
+ };
+
+ const userListProps = {
+ dataSource: list,
+ loading,
+ total,
+ current,
+ onPageChange(page) {
+ dispatch(routerRedux.push({
+ pathname: '/users',
+ query: { page },
+ }));
+ },
+ onDeleteItem(id) {
+ dispatch({
+ type: 'users/delete',
+ payload: id,
+ });
+ },
+ onEditItem(item) {
+ dispatch({
+ type: 'users/showModal',
+ payload: {
+ modalType: 'update',
+ currentItem: item,
+ },
+ });
+ },
+ };
+
+ const userSearchProps = {
+ field,
+ keyword,
+ onSearch(fieldsValue) {
+ dispatch({
+ type: 'users/query',
+ payload: fieldsValue,
+ });
+ },
+ onAdd() {
+ dispatch({
+ type: 'users/showModal',
+ payload: {
+ modalType: 'create',
+ },
+ });
+ },
+ };
+
+ // 解决 Form.create initialValue 的问题
+ // 每次创建一个全新的组件, 而不做diff
+ // 如果你使用了redux, 请移步 http://react-component.github.io/form/examples/redux.html
+ const UserModalGen = () =>
+ ;
+
+ return (
+
+
+
+
+
+
+
+ );
+}
+
+Users.propTypes = {
+ users: PropTypes.object,
+ location: PropTypes.object,
+ dispatch: PropTypes.func,
+};
+
+function mapStateToProps({ users }) {
+ return { users };
+}
+
+export default connect(mapStateToProps)(Users);
diff --git a/src/routes/Users.less b/src/routes/Users.less
new file mode 100644
index 0000000..8803a93
--- /dev/null
+++ b/src/routes/Users.less
@@ -0,0 +1,4 @@
+.normal {
+ width: 800px;
+ margin: 20px auto;
+}
diff --git a/src/services/example.js b/src/services/example.js
deleted file mode 100644
index bbf54e3..0000000
--- a/src/services/example.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import request from '../utils/request';
-
-export async function query() {
- return request('/api/users');
-}
diff --git a/src/services/users.js b/src/services/users.js
new file mode 100644
index 0000000..47126d5
--- /dev/null
+++ b/src/services/users.js
@@ -0,0 +1,27 @@
+import request from '../utils/request';
+import qs from 'qs';
+
+export async function query(params) {
+ return request(`/api/users?${qs.stringify(params)}`);
+}
+
+export async function create(params) {
+ return request('/api/users', {
+ method: 'post',
+ body: qs.stringify(params),
+ });
+}
+
+export async function remove(params) {
+ return request('/api/users', {
+ method: 'delete',
+ body: qs.stringify(params),
+ });
+}
+
+export async function update(params) {
+ return request('/api/users', {
+ method: 'put',
+ body: qs.stringify(params),
+ });
+}
diff --git a/src/utils/request.js b/src/utils/request.js
new file mode 100644
index 0000000..c808422
--- /dev/null
+++ b/src/utils/request.js
@@ -0,0 +1,30 @@
+import fetch from 'dva/fetch';
+
+function parseJSON(response) {
+ return response.json();
+}
+
+function checkStatus(response) {
+ if (response.status >= 200 && response.status < 300) {
+ return response;
+ }
+
+ const error = new Error(response.statusText);
+ error.response = response;
+ throw error;
+}
+
+/**
+ * Requests a URL, returning a promise.
+ *
+ * @param {string} url The URL we want to request
+ * @param {object} [options] The options we want to pass to "fetch"
+ * @return {object} An object containing either "data" or "err"
+ */
+export default function request(url, options) {
+ return fetch(url, options)
+ .then(checkStatus)
+ .then(parseJSON)
+ .then((data) => ({ data }))
+ .catch((err) => ({ err }));
+}