Antd-Pro 如何定义一个菜单并且mock数据到页面
1、Antd-Pro介绍
Ant Design Pro 是一个企业级中后台前端/设计解决方案,我们秉承 Ant Design 的设计价值观,致力于在设计规范和基础组件的基础上,继续向上构建,提炼出典型模板/业务组件/配套设计资源,进一步提升企业级中后台产品设计研发过程中的『用户』和『设计者』的体验。随着『设计者』的不断反馈,我们将持续迭代,逐步沉淀和总结出更多设计模式和相应的代码实现,阐述中后台产品模板/组件/业务场景的最佳实践,也十分期待你的参与和共建。
2、前期准备
Antd-pro的技术栈基于 ES2015+、React、dva、g2 和 antd,提前了解和学习这些知识会非常有帮助。官方的脚手架是基于dva-cli搭建的,所以了解dva是必须,否则很多代码是看不懂的,没有概念的自行百度去脑补一下。3、环境搭建
从官方提供的git地址下载代码就可以了,用vscode打开代码。- 配置好registry
- npm i 安装相关依赖
npm run build 编译成功
4、添加一个菜单
-
=》 pims-web/src/common/menu.js。 这个文件默认定义了菜单结构的json,我们加一个Demo1-节点类型的菜单。
menu.js{
name:
'Demo1'
,
icon:
'user'
,
path:
'demo1'
,
authority:
'admin'
,
children: [
{
name:
'节点类型'
,
path:
'demo11'
,
}
],
},
- =》pims-web/src/common/router.js。 router.js是负责整个工程的路由事件。[“factoryTypes”],这个参数是我们新建的store,用来接收action的请求,这个是dva的特性,此处可以将我们的component与model绑定起来,实现state的更新和传递。
'/demo1/demo11' : {
component: dynamicWrapper(app, [ "factoryTypes" ], () => import ( '../routes/Demo1/demo11' )),
},
|
5、准备mock数据及配置
-
=》pims-web/mock/api.js。 这个文件里面定义了很多常量,用来初始化一些通用的数据。对于整个系统来说,请求接口是复杂并且繁多的,为了处理大量模拟请求的场景,我们也可以在mock文件夹下单独新建一个mock文件,根据业务需求来mock数据。这里已工厂模型类型为例:
api.js//罗凯——工厂模型类型
export
const
getFactoryTypes = [{
key:
'0'
,
name:
'工厂'
,
}, {
key:
'1'
,
name:
'车间'
,
},
{
key:
'2'
,
name:
'工段'
,
}];
export
default
{
getNotice,
getActivities,
getFakeList,
getFactoryTypes,
};
我们定义了一个叫 getFactoryTypes的常量,初始化了三条数据。然后在export中将我们定义的常量输出。 -
=》pims-web/.roadhogrc.mock.js。 添加代理请求,此处使用了roadhog的请求代理功能来处理代理请求,支持基于require动态分析的实时刷新,支持ES6语法。
'GET /api/factoryTypes'
: getFactoryTypes,
当客户端(浏览器)发送请求,如:
GET /api/factoryTypes
,那么本地启动的roadhog server
会跟此配置文件匹配请求路径以及方法,如果匹配到了,就会将请求通过配置处理,就可以像样例一样,你可以直接返回数据,也可以通过函数处理以及重定向到另一个服务器。尝试请求接口,浏览器正常返回数据,说明配置已经成功了。
- =》pims-web/src/services/api.js.。调用request模块将我们的接口封装成一个异步接口,并export。
export async function queryFactoryTypes() { return request( '/api/factoryTypes' );
} |
6、如何请求数据
简单的话实际上直接通过ajax调用我们上面封装的api地址就可以直接拿到数据了,但是antd-pro引入了dva(redux)的概念,让我们能够更方便的进行状态管理。
redux解决了什么样的问题呢:
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式(比如普通用户和管理员)
- 多个用户之间可以协作
- 与服务器大量交互,或者使用了WebSocket
- View要从多个来源获取数据
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
redux可参考阮一峰的【Redux的基本用法】:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html。
-
=》pims-web/src/services/model/factoryTypes.js.。 建立我们自己的model,来接收action,更新state并传递给props。
import
{ queryFactoryTypes } from
'../services/api'
;
export
default
{
namespace:
'factoryTypes'
,
state: {
list: [],
},
effects: {
*fetchList(_, { call, put }) {
const
response = yield call(queryFactoryTypes);
//调用函数,成功后触发 `saveList` action,保存数据到 state
yield put({
type:
'saveList'
,
payload: Array.isArray(response) ? response : [],
});
},
},
reducers: {
saveList(state, action) {
//保存数据到state
return
{
...state,
list: action.payload,
};
},
},
};
前面我们新建了一个菜单,当然也要新建对应的页面文件和样式文件。=》pims-web/src/routes/Demo1.
.
import React, {PureComponent} from 'react' ;
import ReactDom from 'react-dom' ;
import {Card, Layout, Table, Input, Icon, Button, Popconfirm } from 'antd' ;
import { connect } from 'dva' ;
var styles = require(`./style.css`); class EditableCell extends React.Component {
state = {
value: this .props.value,
editable: false ,
}
handleChange = (e) => {
const value = e.target.value;
this .setState({ value });
}
check = () => {
this .setState({ editable: false });
if ( this .props.onChange) {
this .props.onChange( this .state.value);
}
}
edit = () => {
this .setState({ editable: true });
}
render() {
const { value, editable } = this .state;
return (
<div className={styles[ "editable-cell" ]}>
{
editable ?
<div className={styles[ "editable-cell-input-wrapper" ]}>
<Input
value={value}
onChange={ this .handleChange}
onPressEnter={ this .check}
/>
<Icon
type= "check"
className={styles[ "editable-cell-icon-check" ]}
onClick={ this .check}
/>
</div>
:
<div className={styles[ "editable-cell-text-wrapper" ]}>
{value || ' ' }
<Icon
type= "edit"
className={styles[ "editable-cell-icon" ]}
onClick={ this .edit}
/>
</div>
}
</div>
);
}
}
//connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
@connect (({factoryTypes, loading }) => (
{
factoryTypes,
factoryTypesLoading: loading.effects[ 'factoryTypes/fetchList' ],
}
))
class EditableTable extends React.Component {
componentDidMount() {
const { dispatch } = this .props;
dispatch({
type: 'factoryTypes/fetchList' ,
});
}
constructor(props) {
super (props);
this .columns = [{
title: '类型编号' ,
dataIndex: 'key' ,
width: '15%' ,
}, {
title: '类型名称' ,
dataIndex: 'name' ,
width: '15%' ,
render: (text, record) => (
<EditableCell
value={text}
onChange={ this .onCellChange(record.name, 'name' )}
/>
),
}, {
title: '操作' ,
dataIndex: 'operation' ,
render: (text, record) => {
return (
this .state.dataSource.length > 1 ?
(
<Popconfirm title= "Sure to delete?" onConfirm={() => this .onDelete(record.key)}>
<a href= "javascript:;" >删除</a>
</Popconfirm>
) : null
);
},
}];
this .state = {
dataSource: "" ,
count: 0 ,
};
}
onCellChange = (key, dataIndex) => {
return (value) => {
const dataSource = [... this .state.dataSource];
const target = dataSource.find(item => item.key === key);
if (target) {
target[dataIndex] = value;
this .setState({ dataSource });
}
};
}
onDelete = (key) => {
const dataSource = [... this .state.dataSource];
this .setState({ dataSource: dataSource.filter(item => item.key !== key) });
}
handleAdd = () => {
const { count, dataSource } = this .state;
const newData = {
key: count,
name: `Edward King ${count}`,
};
this .setState({
dataSource: [...dataSource, newData],
count: count + 1 ,
});
}
componentWillReceiveProps=()=>{
const { factoryTypes: { list } } = this .props;
this .setState({dataSource: list,count: 3 });
}
render() {
const { dataSource } = this .state;
const columns = this .columns;
return (
<Card bordered={ false }>
<Button className={styles[ "editable-add-btn" ]} onClick={ this .handleAdd}>添加</Button>
<Table bordered dataSource={dataSource} columns={columns} size= "small" />
</Card>
);
}
}
export default () => (
<EditableTable />
); |
- react-redux 的文档中,对 @
connect
的描述是一段很难理解的英文,我一直没有搞清楚这个东西的用法。大致的作用就是连接React组件与 Redux store,也就是我们前面定义的model。 - 在组建render完成之后调用 componentDidMount 函数,我们通过 dispatch 来触发action,从而来更新state并且传递给组件的props。
-
最后在组件接收到的props参数后调用 componentWillReceiveProps 获取更新后的state,根据新的state来初始化组件内部的state,完成页面的数据绑定。
/* @import '~antd/lib/style/themes/default.less'; @import '../../utils/utils.less'; */ .editable-cell { position: relative;
} .editable-cell-input-wrapper, .editable-cell-text-wrapper { padding-right: 24px;
} .editable-cell-text-wrapper { padding: 5px 24px 5px 5px;
} .editable-cell-icon, .editable-cell-icon-check { position: absolute;
right: 0 ;
width: 20px;
cursor: pointer;
} .editable-cell-icon { line-height: 18px;
display: none;
} .editable-cell-icon-check { line-height: 28px;
} .editable-cell:hover .editable-cell-icon { display: inline-block;
} .editable-cell-icon:hover, .editable-cell-icon-check:hover { color: #108ee9;
} .editable-add-btn { margin-bottom: 8px;
} |