同事写了一个我目前看着比较正规化的组件式页面,在此作为学习标准贴一下,先看个效果图:
这是一个oauth的client管理的页面,主要代码如下:
api列表数据结构
{
"code": 0,
"message": "操作成功",
"data": {
"content": [
{
"clientId": "usercenter-manage",
"clientName": "测试app",
"resourceIds": "usercenter/manage,smscenter/api",
"clientSecret": "",
"scope": "read,write,trust",
"authorizedGrantTypes": "password,refresh_token",
"webServerRedirectUri": null,
"authorities": null,
"accessTokenValidity": 7200,
"refreshTokenValidity": null,
"additionalInformation": null,
"autoapprove": null,
"smsCodeLength": 4,
"smsCodeExpire": 10,
"smsCodeSign": "【xxxxxx】",
"platformCode": null,
"updateTime": "2019-03-25T14:27:26.000+0000",
"createTime": null
}
],
"pageable": {
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"offset": 0,
"pageSize": 10,
"pageNumber": 0,
"paged": true,
"unpaged": false
},
"totalPages": 1,
"totalElements": 9,
"last": true,
"size": 10,
"number": 0,
"first": true,
"numberOfElements": 9,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"empty": false
}
}
组件化嘛,文件自然比较多,打个标识
1、ClientList:页面渲染js
import React, { PureComponent } from 'react';
import { connect } from 'dva';
import { Popconfirm, Card, Table, Button, Divider, Tag, message } from 'antd';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import DescriptionList from '@/components/DescriptionList';
import ClientForm from './ClientForm';
import { humanizeTime, getOAuthTypeNames } from './ClientUtil';
import styles from '../Common/TableList.less';
const { Description } = DescriptionList;
@connect(({ client, loading }) => ({
client,
loading: loading.effects['client/fetch'],
submitting: loading.effects['client/update'],
}))
class ClientList extends PureComponent {
columns = [
{
title: '应用ID',
dataIndex: 'clientId',
},
{
title: '应用名称',
dataIndex: 'clientName',
},
{
title: (
Token
有效期
),
dataIndex: 'accessTokenValidity',
render: value => humanizeTime(value),
},
{
title: (
RefreshToken
有效期
),
dataIndex: 'refreshTokenValidity',
render: value => humanizeTime(value),
},
{
title: '操作',
dataIndex: 'action',
render: (text, record) => (
{
this.handleOpenForm(record);
}}
>
编辑
{
this.handleRemove(record);
}}
okText="确认"
cancelText="取消"
>
删除
),
},
];
componentDidMount() {
const { dispatch } = this.props;
dispatch({ type: 'client/fetch' });
}
handleTableChange = pagination => {
const { current, pageSize } = pagination;
const { dispatch } = this.props;
dispatch({ type: 'client/fetch', payload: { page: current - 1, size: pageSize } });
};
handleTableExpand = record => {
const toTags = items => items.map((value, index) => {value} );
return (
{toTags(getOAuthTypeNames(record.authorizedGrantTypes))}
{record.platformCode}
{record.resourceIds}
{record.smsCodeLength}
{record.smsCodeLength ? `${record.smsCodeLength}分钟` : ''}
{record.smsCodeSign}
);
};
handleOpenForm = formData => {
const { dispatch } = this.props;
dispatch({ type: 'client/openForm', payload: formData });
};
handleCloseForm = () => {
const { dispatch } = this.props;
dispatch({ type: 'client/closeForm' });
};
handleAdd = values => {
const { dispatch } = this.props;
dispatch({ type: 'client/update', payload: values }).then(() => {
message.success('操作成功');
});
};
handleRemove = record => {
const { dispatch } = this.props;
dispatch({ type: 'client/remove', payload: record.clientId }).then(() => {
message.success('删除成功');
});
};
render() {
const {
client: { list, form },
loading,
submitting,
} = this.props;
const paginationProps = {
showSizeChanger: true,
showQuickJumper: true,
...list.pagination,
};
return (
);
}
}
export default ClientList;
2、ClientForm:添加编辑单条数据的Form
import React, { PureComponent } from 'react';
import { Modal, Form, Input, Tabs, InputNumber } from 'antd';
import SecretInput from './SecretInput';
import PeriodInput from './PeriodInput';
import GrantTypeInput from './GrantTypeInput';
import SmsSignInput from './SmsSignInput';
const { Item: FormItem } = Form;
const { TabPane } = Tabs;
@Form.create()
class ClientForm extends PureComponent {
state = {
tabKey: '1',
};
reset = () => {
const { form } = this.props;
form.resetFields();
this.setState({ tabKey: '1' });
};
render() {
const { data, visible, submitting, onSave, onClose, form } = this.props;
const { tabKey } = this.state;
const formItemLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 15 },
};
const title = data.clientId ? '更新应用' : '添加应用';
return (
{
this.reset();
onClose();
}}
onOk={() => {
form.validateFields((err, values) => {
if (!err) onSave(values);
else this.setState({ tabKey: '1' });
});
}}
>
);
}
}
export default ClientForm;
3、ClientUtil.js
import _ from 'lodash';
import moment from 'moment';
export const authTypes = [
{ name: '授权码模式', value: 'authorization_code' },
{ name: '简化模式', value: 'implicit' },
{ name: '密码模式', value: 'password' },
{ name: '客户端模式', value: 'client_credentials' },
{ name: '刷新模式', value: 'refresh_token' },
];
export function getOAuthTypeNames(str) {
if (!str) return [];
const values = str.split(',');
return values.map(value => _.find(authTypes, t => t.value === value).name);
}
export function getResources(str) {
if (!str) return [];
return str.split(',');
}
export function humanizeTime(value) {
let timeText = '';
if (value) {
timeText = moment.duration(value, 'seconds').humanize();
} else {
timeText = '未设置';
}
return timeText;
}
4、GrantTypeInput组件GrantTypeInput.js
import React, { PureComponent } from 'react';
import { Select } from 'antd';
import { authTypes } from './ClientUtil';
const { Option } = Select;
class GrantTypeInput extends PureComponent {
state = {
value: [],
};
componentWillMount() {
const { props } = this;
if (props.value) {
const value = props.value ? props.value.split(',') : [];
this.setState({ value });
}
}
componentWillReceiveProps(nextProps) {
const { props } = this;
if (props.value !== nextProps.value && !nextProps.value) {
this.setState({ value: [] });
} else {
const value = nextProps.value ? nextProps.value.split(',') : [];
this.setState({ value });
}
}
handleSelectChange = value => {
const { onChange } = this.props;
this.setState({ value });
if (onChange) onChange(value.join(','));
};
render() {
const { value } = this.state;
return (
);
}
}
export default GrantTypeInput;
5、token有效期输入组件PeriodInput.js
import React, { PureComponent } from 'react';
import moment from 'moment';
import { Input, Select } from 'antd';
const { Option } = Select;
const getTime = (time, fromUnit, toUnit) => moment.duration(Number(time), fromUnit).as(toUnit);
export const timeUnits = [
{ name: '秒', value: 'seconds' },
{ name: '分钟', value: 'minutes' },
{ name: '小时', value: 'hours' },
{ name: '天', value: 'days' },
];
class PeriodInput extends PureComponent {
state = {
value: '',
unit: 'seconds',
};
componentWillMount() {
const { props } = this;
if (props.value) {
this.setState({
unit: 'seconds',
value: props.value,
});
}
}
componentWillReceiveProps(nextProps) {
const { props } = this;
if (props.value !== nextProps.value && !nextProps.value) {
this.setState({
unit: 'seconds',
value: '',
});
} else {
this.setState({
unit: 'seconds',
value: nextProps.value,
});
}
}
handleSelectChange = unitValue => {
const { unit, value } = this.state;
const newValue = value ? getTime(value, unit, unitValue) : '';
this.setState({
unit: unitValue,
value: newValue,
});
};
onChangeValue = e => {
const { value } = e.target;
const { unit } = this.state;
const { onChange } = this.props;
const seconds = getTime(value, unit, 'seconds');
this.setState({ value });
if (onChange) onChange(seconds);
};
render() {
const { unit, value } = this.state;
return (
{timeUnits.map(t => (
))}
}
/>
);
}
}
export default PeriodInput;
6、秘钥生成组件SecretInput.js
import React, { PureComponent } from 'react';
import { Row, Col, Slider, Input } from 'antd';
import random from '@/utils/random';
const minValue = 10;
const maxValue = 30;
const defaultState = {
visible: false,
value: '',
length: 10,
};
class SecretInput extends PureComponent {
state = { ...defaultState };
componentWillMount() {
const { props } = this;
if (props.value) {
this.setState({ value: props.value });
}
}
componentWillReceiveProps(nextProps) {
const { props } = this;
if (props.value !== nextProps.value && !nextProps.value) {
this.setState({ ...defaultState });
} else {
this.setState({ value: nextProps.value });
}
}
handleCreatePwd = () => {
const { length } = this.state;
this.setState({ visible: true });
this.handleChangeLength(length);
};
handleChangeLength = length => {
const value = random.generate(length);
this.setState({ length });
this.handleChangeValue(value);
};
handleChangeValue = value => {
const { onChange } = this.props;
this.setState({ value });
if (onChange) onChange(value);
};
render() {
const { visible, value, length } = this.state;
return (
随机生成
{visible ? (
) : null}
);
}
}
export default SecretInput;
7、短信签名:输入内容与数据库保存不一致,前端正则加减括号
import React, { PureComponent } from 'react';
import { Input } from 'antd';
const removeBrackets = value => (value ? value.replace(/[【】]/g, '') : '');
const addBrackets = value => (value ? `【${value}】` : '');
class SmsSignInput extends PureComponent {
state = {
value: '',
};
componentWillMount() {
const { props } = this;
if (props.value) this.setStateValue(props.value);
}
componentWillReceiveProps(nextProps) {
const { props } = this;
if (props.value !== nextProps.value && !nextProps.value) {
this.setStateValue('');
} else {
this.setStateValue(nextProps.value);
}
}
setStateValue = value => {
this.setState({ value: removeBrackets(value) });
};
handleChangeValue = e => {
const { value } = e.target;
const { onChange } = this.props;
this.setState({ value });
if (onChange) onChange(addBrackets(value));
};
render() {
const { value } = this.state;
const { props } = this;
return ;
}
}
export default SmsSignInput;