提交 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 | 2 | "name": "@umi-block/blank", |
| 3 | - "version": "0.0.1", | |
| 4 | 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 | 4 | "dependencies": { |
| 14 | - "antd": "^3.0.0", | |
| 15 | - "dva": "^2.0.0", | |
| 16 | - "react": ">=16.0.0", | |
| 17 | 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 | 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 | 25 | "prettier": "1.15.2", |
| 26 | 26 | "stylelint": "^9.8.0", |
| 27 | 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 | 33 | "lint-staged": { |
| 31 | 34 | "x/**/*.{js,ts,tsx,json,jsx,less}": [ |
| 32 | 35 | "node ./_scripts/lint-prettier.js", |
| 33 | 36 | "git add" |
| 34 | 37 | ], |
| 35 | - "**/*.{js,jsx}": "npm run lint-staged:js", | |
| 38 | + "x/**/*.{js,jsx}": "npm run lint-staged:js", | |
| 36 | 39 | "**/*.less": "stylelint --syntax less" |
| 37 | 40 | }, |
| 38 | 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
请
注册
或
登录
后发表评论