提交 f78c36093da26251e71c75ac4a2f8582fe37b758

作者 chencheng (云谦)
提交者 Yu
1 个父辈 5609d81a

feat: add user dashboard and simplify the block (#13)

1 -The MIT License (MIT)  
2 -  
3 -Copyright (c) 2018-present yutingzhao1991 (yutingzhao1991@sina.com)  
4 -  
5 -Permission is hereby granted, free of charge, to any person obtaining a copy  
6 -of this software and associated documentation files (the "Software"), to deal  
7 -in the Software without restriction, including without limitation the rights  
8 -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
9 -copies of the Software, and to permit persons to whom the Software is  
10 -furnished to do so, subject to the following conditions:  
11 -  
12 -The above copyright notice and this permission notice shall be included in  
13 -all copies or substantial portions of the Software.  
14 -  
15 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
16 -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  
17 -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  
18 -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
19 -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  
20 -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN  
21 -THE SOFTWARE.  
1 { 1 {
2 "name": "@umi-block/blank", 2 "name": "@umi-block/blank",
3 - "version": "0.0.1",  
4 "description": "A blank block for quick start a umi page develop.", 3 "description": "A blank block for quick start a umi page develop.",
5 - "main": "src/index.js",  
6 - "scripts": {  
7 - "dev": "umi dev"  
8 - },  
9 - "repository": {  
10 - "type": "git",  
11 - "url": "https://github.com/umijs/umi-blocks/blank"  
12 - },  
13 "dependencies": { 4 "dependencies": {
14 - "antd": "^3.0.0",  
15 - "dva": "^2.0.0",  
16 - "react": ">=16.0.0",  
17 "umi-request": "^1.0.0" 5 "umi-request": "^1.0.0"
18 - },  
19 - "devDependencies": {  
20 - "umi": "^2.3.0-beta.1",  
21 - "umi-plugin-block-dev": "^1.0.0",  
22 - "umi-plugin-react": "^1.3.0-beta.1"  
23 - },  
24 - "authors": [  
25 - "yutingzhao1991 <yutingzhao1991@sina.com>"  
26 - ],  
27 - "license": "ISC" 6 + }
28 } 7 }
1 { 1 {
2 "name": "@umi-blocks/demo", 2 "name": "@umi-blocks/demo",
3 - "version": "1.0.0",  
4 - "description": "a demo block of umi",  
5 - "main": "src/index.js",  
6 - "scripts": {  
7 - "dev": "umi dev"  
8 - },  
9 - "repository": {  
10 - "type": "git",  
11 - "url": "https://github.com/umijs/umi-blocks/demo"  
12 - },  
13 - "dependencies": {  
14 - "react": ">=16.0.0",  
15 - "antd": "^3.0.0"  
16 - },  
17 - "devDependencies": {  
18 - "umi": "^2.0.0",  
19 - "umi-plugin-block-dev": "^1.0.0",  
20 - "umi-plugin-react": "^1.2.3"  
21 - },  
22 - "author": "yutingzhao1991 <yutingzhao1991@sina.com>",  
23 - "license": "ISC" 3 + "description": "a demo block of umi"
24 } 4 }
@@ -25,14 +25,17 @@ @@ -25,14 +25,17 @@
25 "prettier": "1.15.2", 25 "prettier": "1.15.2",
26 "stylelint": "^9.8.0", 26 "stylelint": "^9.8.0",
27 "stylelint-config-prettier": "^4.0.0", 27 "stylelint-config-prettier": "^4.0.0",
28 - "stylelint-config-standard": "^18.0.0" 28 + "stylelint-config-standard": "^18.0.0",
  29 + "umi": "^2.3.0-0",
  30 + "umi-plugin-block-dev": "^1.0.0",
  31 + "umi-plugin-react": "^1.3.0-0"
29 }, 32 },
30 "lint-staged": { 33 "lint-staged": {
31 "x/**/*.{js,ts,tsx,json,jsx,less}": [ 34 "x/**/*.{js,ts,tsx,json,jsx,less}": [
32 "node ./_scripts/lint-prettier.js", 35 "node ./_scripts/lint-prettier.js",
33 "git add" 36 "git add"
34 ], 37 ],
35 - "**/*.{js,jsx}": "npm run lint-staged:js", 38 + "x/**/*.{js,jsx}": "npm run lint-staged:js",
36 "**/*.less": "stylelint --syntax less" 39 "**/*.less": "stylelint --syntax less"
37 }, 40 },
38 "husky": { 41 "husky": {
  1 +export default {
  2 + plugins: [
  3 + ['umi-plugin-block-dev', {}],
  4 + ['umi-plugin-react', {
  5 + antd: true,
  6 + dva: true,
  7 + }],
  8 + ],
  9 +}
  1 +{
  2 + "name": "@umi-block/user-dashboard",
  3 + "description": "User dashboard example.",
  4 + "dependencies": {
  5 + "umi-request": "^1.0.0"
  6 + }
  7 +}
  1 +
  2 +const PAGE_SIZE = 3;
  3 +let data = [
  4 + {
  5 + "id": 1,
  6 + "name": "Leanne Graham",
  7 + "email": "Sincere@april.biz",
  8 + "website": "hildegard.org",
  9 + },
  10 + {
  11 + "id": 2,
  12 + "name": "Ervin Howell",
  13 + "email": "Shanna@melissa.tv",
  14 + "website": "anastasia.net",
  15 + },
  16 + {
  17 + "id": 3,
  18 + "name": "Clementine Bauch",
  19 + "email": "Nathan@yesenia.net",
  20 + "website": "ramiro.info",
  21 + },
  22 + {
  23 + "id": 4,
  24 + "name": "Patricia Lebsack",
  25 + "email": "Julianne.OConner@kory.org",
  26 + "website": "kale.biz",
  27 + },
  28 + {
  29 + "id": 5,
  30 + "name": "Chelsey Dietrich",
  31 + "email": "Lucio_Hettinger@annie.ca",
  32 + "website": "demarco.info",
  33 + },
  34 + {
  35 + "id": 6,
  36 + "name": "Mrs. Dennis Schulist",
  37 + "email": "Karley_Dach@jasper.info",
  38 + "website": "ola.org",
  39 + },
  40 + {
  41 + "id": 7,
  42 + "name": "Kurtis Weissnat",
  43 + "email": "Telly.Hoeger@billy.biz",
  44 + "website": "elvis.io",
  45 + },
  46 + {
  47 + "id": 8,
  48 + "name": "Nicholas Runolfsdottir V",
  49 + "email": "Sherwood@rosamond.me",
  50 + "website": "jacynthe.com",
  51 + },
  52 + {
  53 + "id": 9,
  54 + "name": "Glenna Reichert",
  55 + "email": "Chaim_McDermott@dana.io",
  56 + "website": "conrad.com",
  57 + },
  58 +];
  59 +
  60 +function uid(len) {
  61 + len = len || 7;
  62 + return Math.random().toString(35).substr(2, len);
  63 +}
  64 +
  65 +function getData(page) {
  66 + const start = (page - 1) * PAGE_SIZE;
  67 + return {
  68 + status: 'success',
  69 + total: data.length,
  70 + page,
  71 + data: data.slice(start, start + 3),
  72 + };
  73 +}
  74 +
  75 +export default {
  76 + 'GET /api/BLOCK_NAME': (req, res) => {
  77 + res.json(getData(parseInt(req.query.page, 10) || 1));
  78 + },
  79 + 'DELETE /api/BLOCK_NAME/:id': (req, res) => {
  80 + data = data.filter(item => `${item.id}` !== `${req.params.id}`);
  81 + res.end('ok');
  82 + },
  83 + 'PATCH /api/BLOCK_NAME/:id': (req, res) => {
  84 + data.forEach(item => {
  85 + if (`${item.id}` === `${req.params.id}`) {
  86 + Object.assign(item, req.body);
  87 + }
  88 + });
  89 + res.end('ok');
  90 + },
  91 + 'POST /api/BLOCK_NAME': (req, res) => {
  92 + data.push({
  93 + ...req.body,
  94 + id: uid(),
  95 + });
  96 + res.end('ok');
  97 + },
  98 +};
  1 +import { Component } from 'react';
  2 +import { Modal, Form, Input } from 'antd';
  3 +
  4 +const FormItem = Form.Item;
  5 +
  6 +class UserEditModal extends Component {
  7 + constructor(props) {
  8 + super(props);
  9 + this.state = {
  10 + visible: false,
  11 + };
  12 + }
  13 +
  14 + showModelHandler = (e) => {
  15 + if (e) e.stopPropagation();
  16 + this.setState({
  17 + visible: true,
  18 + });
  19 + };
  20 +
  21 + hideModelHandler = () => {
  22 + this.setState({
  23 + visible: false,
  24 + });
  25 + };
  26 +
  27 + okHandler = () => {
  28 + const { onOk } = this.props;
  29 + this.props.form.validateFields((err, values) => {
  30 + if (!err) {
  31 + onOk(values);
  32 + this.hideModelHandler();
  33 + }
  34 + });
  35 + };
  36 +
  37 + render() {
  38 + const { children } = this.props;
  39 + const { getFieldDecorator } = this.props.form;
  40 + const { name, email, website } = this.props.record;
  41 + const formItemLayout = {
  42 + labelCol: { span: 6 },
  43 + wrapperCol: { span: 14 },
  44 + };
  45 +
  46 + return (
  47 + <span>
  48 + <span onClick={this.showModelHandler}>
  49 + { children }
  50 + </span>
  51 + <Modal
  52 + title="Edit User"
  53 + visible={this.state.visible}
  54 + onOk={this.okHandler}
  55 + onCancel={this.hideModelHandler}
  56 + >
  57 + <Form horizontal onSubmit={this.okHandler}>
  58 + <FormItem
  59 + {...formItemLayout}
  60 + label="Name"
  61 + >
  62 + {
  63 + getFieldDecorator('name', {
  64 + initialValue: name,
  65 + })(<Input />)
  66 + }
  67 + </FormItem>
  68 + <FormItem
  69 + {...formItemLayout}
  70 + label="Email"
  71 + >
  72 + {
  73 + getFieldDecorator('email', {
  74 + initialValue: email,
  75 + })(<Input />)
  76 + }
  77 + </FormItem>
  78 + <FormItem
  79 + {...formItemLayout}
  80 + label="Website"
  81 + >
  82 + {
  83 + getFieldDecorator('website', {
  84 + initialValue: website,
  85 + })(<Input />)
  86 + }
  87 + </FormItem>
  88 + </Form>
  89 + </Modal>
  90 + </span>
  91 + );
  92 + }
  93 +}
  94 +
  95 +export default Form.create()(UserEditModal);
  1 +
  2 +.normal {
  3 +}
  4 +
  5 +.operation a {
  6 + margin: 0 .5em;
  7 +}
  8 +
  9 +.create {
  10 + margin: 1.5em 0;
  11 +}
  1 +import { connect } from 'dva';
  2 +import { Table, Pagination, Popconfirm, Button } from 'antd';
  3 +import { routerRedux } from 'dva/router';
  4 +import styles from './Users.css';
  5 +import { PAGE_SIZE } from '../utils/constants';
  6 +import UserModal from './UserModal';
  7 +
  8 +function Users({ dispatch, list: dataSource, loading, total, page: current }) {
  9 + function deleteHandler(id) {
  10 + dispatch({
  11 + type: 'BLOCK_NAME/remove',
  12 + payload: id,
  13 + });
  14 + }
  15 +
  16 + function pageChangeHandler(page) {
  17 + dispatch({
  18 + type: 'BLOCK_NAME/fetch',
  19 + payload: { page },
  20 + });
  21 + }
  22 +
  23 + function editHandler(id, values) {
  24 + dispatch({
  25 + type: 'BLOCK_NAME/patch',
  26 + payload: { id, values },
  27 + });
  28 + }
  29 +
  30 + function createHandler(values) {
  31 + dispatch({
  32 + type: 'BLOCK_NAME/create',
  33 + payload: values,
  34 + });
  35 + }
  36 +
  37 + const columns = [
  38 + {
  39 + title: 'Name',
  40 + dataIndex: 'name',
  41 + key: 'name',
  42 + render: text => <a href="">{text}</a>,
  43 + },
  44 + {
  45 + title: 'Email',
  46 + dataIndex: 'email',
  47 + key: 'email',
  48 + },
  49 + {
  50 + title: 'Website',
  51 + dataIndex: 'website',
  52 + key: 'website',
  53 + },
  54 + {
  55 + title: 'Operation',
  56 + key: 'operation',
  57 + render: (text, record) => (
  58 + <span className={styles.operation}>
  59 + <UserModal record={record} onOk={editHandler.bind(null, record.id)}>
  60 + <a>Edit</a>
  61 + </UserModal>
  62 + <Popconfirm title="Confirm to delete?" onConfirm={deleteHandler.bind(null, record.id)}>
  63 + <a href="">Delete</a>
  64 + </Popconfirm>
  65 + </span>
  66 + ),
  67 + },
  68 + ];
  69 +
  70 + return (
  71 + <div className={styles.normal}>
  72 + <div>
  73 + <div className={styles.create}>
  74 + <UserModal record={{}} onOk={createHandler}>
  75 + <Button type="primary">Create User</Button>
  76 + </UserModal>
  77 + </div>
  78 + <Table
  79 + loading={loading}
  80 + columns={columns}
  81 + dataSource={dataSource}
  82 + rowKey={record => record.id}
  83 + pagination={false}
  84 + />
  85 + <Pagination
  86 + className="ant-table-pagination"
  87 + total={total}
  88 + current={current}
  89 + pageSize={PAGE_SIZE}
  90 + onChange={pageChangeHandler}
  91 + />
  92 + </div>
  93 + </div>
  94 + );
  95 +}
  96 +
  97 +function mapStateToProps(state) {
  98 + const { list, total, page } = state['BLOCK_NAME'];
  99 + return {
  100 + list,
  101 + total,
  102 + page,
  103 + loading: state.loading.models['BLOCK_NAME'],
  104 + };
  105 +}
  106 +
  107 +export default connect(mapStateToProps)(Users);
  1 +import { Component } from 'react';
  2 +import { connect } from 'dva';
  3 +import Users from './components/Users';
  4 +
  5 +class BLOCK_NAME extends Component {
  6 + componentDidMount() {
  7 + this.props.dispatch({
  8 + type: 'BLOCK_NAME/fetch',
  9 + payload: {
  10 + page: 1,
  11 + },
  12 + });
  13 + }
  14 +
  15 + render() {
  16 + return (
  17 + <div>
  18 + <Users />
  19 + </div>
  20 + );
  21 + }
  22 +}
  23 +
  24 +export default connect()(BLOCK_NAME);
  1 +import * as usersService from './service';
  2 +
  3 +export default {
  4 + namespace: 'BLOCK_NAME',
  5 + state: {
  6 + list: [],
  7 + total: null,
  8 + page: null,
  9 + },
  10 + reducers: {
  11 + save(state, { payload: { data: list, total, page } }) {
  12 + return { ...state, list, total, page };
  13 + },
  14 + },
  15 + effects: {
  16 + *fetch({ payload: { page = 1 } }, { call, put }) {
  17 + const { data, total, page: currentPage } = yield call(usersService.fetch, { page });
  18 + yield put({
  19 + type: 'save',
  20 + payload: {
  21 + data,
  22 + total,
  23 + page: currentPage,
  24 + },
  25 + });
  26 + },
  27 + *remove({ payload: id }, { call, put, select }) {
  28 + yield call(usersService.remove, id);
  29 + const page = yield select(state => state['BLOCK_NAME'].page);
  30 + yield put({ type: 'fetch', payload: { page } });
  31 + },
  32 + *patch({ payload: { id, values } }, { call, put, select }) {
  33 + yield call(usersService.patch, id, values);
  34 + const page = yield select(state => state['BLOCK_NAME'].page);
  35 + yield put({ type: 'fetch', payload: { page } });
  36 + },
  37 + *create({ payload: values }, { call, put, select }) {
  38 + yield call(usersService.create, values);
  39 + const page = yield select(state => state['BLOCK_NAME'].page);
  40 + yield put({ type: 'fetch', payload: { page } });
  41 + },
  42 + },
  43 +};
  1 +import request from 'umi-request';
  2 +
  3 +const API_PREFIX = `/api/BLOCK_NAME`;
  4 +
  5 +export function fetch({ page = 1 }) {
  6 + return request(`${API_PREFIX}?page=${page}`);
  7 +}
  8 +
  9 +export function remove(id) {
  10 + return request(`${API_PREFIX}/${id}`, {
  11 + method: 'DELETE',
  12 + });
  13 +}
  14 +
  15 +export function patch(id, values) {
  16 + // TODO:
  17 + // use umi-request after the issue is closed
  18 + // https://github.com/umijs/umi-request/issues/5
  19 + return window.fetch(`${API_PREFIX}/${id}`, {
  20 + method: 'PATCH',
  21 + headers: {
  22 + 'Content-Type': 'application/json',
  23 + },
  24 + body: JSON.stringify(values),
  25 + });
  26 +}
  27 +
  28 +export function create(values) {
  29 + return request(API_PREFIX, {
  30 + method: 'POST',
  31 + data: values,
  32 + });
  33 +}
  1 +
  2 +export const PAGE_SIZE = 3;
注册登录 后发表评论