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 ( +
+

Hello Antd.

+
+ +
+ ); +} + +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 = () => +
+
+

404

+

未找到该页面

+ +
+
; + +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 })); +}