提交 f78c36093da26251e71c75ac4a2f8582fe37b758
提交者
Yu
1 个父辈
5609d81a
feat: add user dashboard and simplify the block (#13)
正在显示
15 个修改的文件
包含
436 行增加
和
66 行删除
blank/LICENSE
已删除
100644 → 0
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": { |
user-dashboard/.umirc.js
0 → 100644
user-dashboard/package.json
0 → 100644
user-dashboard/snapshot.png
0 → 100644
85.6 KB
user-dashboard/src/_mock.js
0 → 100644
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 | +}; |
user-dashboard/src/components/UserModal.js
0 → 100644
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); |
user-dashboard/src/components/Users.css
0 → 100644
user-dashboard/src/components/Users.js
0 → 100644
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); |
user-dashboard/src/index.js
0 → 100644
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); |
user-dashboard/src/model.js
0 → 100644
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 | +}; |
user-dashboard/src/service.js
0 → 100644
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 | +} |
user-dashboard/src/utils/constants.js
0 → 100644
请
注册
或
登录
后发表评论