Commit 688aa87b authored by superman's avatar superman

修改了消息推送

parent 68fd4884
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -2,6 +2,6 @@ ...@@ -2,6 +2,6 @@
// - https://github.com/dora-js/dora-plugin-proxy#规则定义 // - https://github.com/dora-js/dora-plugin-proxy#规则定义
module.exports = { module.exports = {
// '/api/*': 'http://react.yanky.cn/', '/api/*': 'http://react.yanky.cn/',
'/api/*': 'http://192.168.1.126:8080/' // '/api/*': 'http://192.168.1.126:8080/'
}; };
...@@ -25,6 +25,7 @@ export default class App extends Component { ...@@ -25,6 +25,7 @@ export default class App extends Component {
const styles = require('./App.less'); const styles = require('./App.less');
const menu = [ const menu = [
{ {
id:'business',
title: '业务管理', title: '业务管理',
icon: 'mail', icon: 'mail',
items: [ items: [
...@@ -79,6 +80,7 @@ export default class App extends Component { ...@@ -79,6 +80,7 @@ export default class App extends Component {
} }
] ]
}, { }, {
id:'base',
title: '基础功能', title: '基础功能',
icon: 'folder', icon: 'folder',
items: [ items: [
...@@ -94,6 +96,7 @@ export default class App extends Component { ...@@ -94,6 +96,7 @@ export default class App extends Component {
} }
] ]
}, { }, {
id:'admin',
title: '管理员', title: '管理员',
icon: 'folder', icon: 'folder',
items: [ items: [
...@@ -110,13 +113,29 @@ export default class App extends Component { ...@@ -110,13 +113,29 @@ export default class App extends Component {
en: 'Create user' en: 'Create user'
} }
] ]
}, {
title: '权限管理',
items: [
{
to: '/admin/resources',
cn: '权限资源列表',
en: ''
}, {
to: '/admin/authorities',
cn: '角色列表',
en: 'Authorities'
}
]
} }
] ]
}]; }];
const {user} = this.props; const {user, location:{pathname}} = this.props;
const logo = require('./images/logo.png'); const logo = require('./images/logo.png');
const openKey = '';
return ( return (
<div className={styles.content}> <div className={styles.content}>
<div className={styles.side}> <div className={styles.side}>
......
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {
Row,
Col,
Form,
Input,
Button,
Checkbox,
Select,
message,
Tabs,
Cascader,
Radio,
Upload,
Icon,
Modal,
DatePicker,
Table,
Spin
} from 'antd';
import Layout from '../../components/Layout/Layout';
import MainHeader from '../../components/MainHeader/MainHeader';
import {
formItemLayout,
smallFormItemLayout,
footerFormSubmitLayout,
userStatusToString,
formatDateTime,
NULL,
ACTIONPERMISSION
} from '../../utils';
@connect(state=>({
loading: state.authority.loading || state.resource.loading,
item: state.authority.item,
isEdit: state.authority.isEdit,
resources: state.resource.items,
resourcesTotal: state.resource.total
}))
@Form.create()
export default class EditItem extends Component {
constructor() {
super(...arguments);
this.state = {
isEdit: false
}
}
componentDidMount() {
this.fetchItem(this.props.params.id);
this.fetchResourceList();
};
componentWillReceiveProps(nextProps) {
this.setState({isEdit: nextProps.isEdit});
};
fetchItem(id) {
this.props.dispatch({
type: 'FETCH_AUTHORITY_ITEM',
id
});
};
fetchResourceList() {
this.props.dispatch({
type: 'FETCH_RESOURCE_LIST'
});
};
handleSubmit(e) {
e.preventDefault();
let data = {};
data = this.props.form.getFieldsValue();
data.id = this.props.params.id;
// if (isNaN(data.status)) {
// STATUS_LIST.forEach(item=> {
// if (item.text === data.status) {
// data.status = item.value
// }
// })
// }
console.log(data);
this.props.dispatch({
type: 'UPDATE_AUTHORITY_ITEM',
item: data
});
}
render() {
const {item, resources, loading, form:{getFieldProps}, location:{query}} = this.props;
const {isEdit} = this.state;
const operation = (
<div style={{textAlign:'right'}}>
<Button.Group>
<Button type="ghost"
onClick={
e=>{
e.preventDefault();
this.props.dispatch({
type: isEdit ? 'CANCEL_UPDATE_AUTHORITY' : 'UPDATE_AUTHORITY'
});
}
}>
<Icon type="edit"/>
</Button>
<Button type="ghost" onClick={e=>{
e.preventDefault();
isEdit ?
this.props.dispatch({
type: 'CANCEL_UPDATE_PRODUCT'
}) :
this.props.history.goBack();
}}>
<Icon type="rollback"/>
</Button>
</Button.Group>
</div>
);
const header = (
<MainHeader breadcrumb={['权限管理', '角色详情']}
title={'角色详情' + (isEdit ? ' - 编辑' : '')}
operation={operation}
/>
);
const resourcesMap = {};
resources.forEach(item=> {
resourcesMap[item.controllerName] = {category: item.category};
});
return (
<Layout header={header}>
<Spin spinning={loading}>
{
item &&
<Form className="main-form" horizontal>
<Form.Item label="角色名" {...smallFormItemLayout}>
{
isEdit ?
<Input {...getFieldProps('name', {initialValue: item.name})} />
:
item.name || NULL
}
</Form.Item>
<Form.Item label="描述" {...smallFormItemLayout}>
{
isEdit ?
<Input {...getFieldProps('description', {initialValue: item.description})} />
:
item.description || NULL
}
</Form.Item>
<Form.Item label="状态" {...smallFormItemLayout}>
{
// isEdit ?
// <Select
// placeholder="请选择状态" {...getFieldProps('status', {initialValue: statusToString(item.status)})} >
// <Select.Option value="1">系统</Select.Option>
// <Select.Option value="5">禁用</Select.Option>
// <Select.Option value="9">启用</Select.Option>
// </Select>
// :
// (typeof item.status !== 'undefined' ? statusToString(item.status) : NULL)
}
</Form.Item>
<Form.Item label="创建时间" {...smallFormItemLayout}>
{item.dateCreated && formatDateTime(item.dateCreated)}
</Form.Item>
<Form.Item label="">
{
Object.keys(resourcesMap).map((resource, index)=>
<div key={'resource-'+index}>{JSON.stringify(resource)}</div>
)
}
</Form.Item>
<Form.Item {...footerFormSubmitLayout} style={{marginTop:30}}>
{
isEdit ?
<Button type="primary" onClick={this.handleSubmit.bind(this)}
loading={loading}>
<Icon type="save"/>保存
</Button>
:
<Button type="primary" onClick={e=>{
e.preventDefault();
this.props.dispatch({
type:'UPDATE_AUTHORITY'
});
}}>
<Icon type="edit"/>编辑
</Button>
}
<Button onClick={e=>{
e.preventDefault();
isEdit ?
this.props.dispatch({
type: 'CANCEL_UPDATE_AUTHORITY'
}) :
this.props.history.goBack();
}}
style={{marginLeft:'1em'}}>
<Icon type="rollback"/>
{isEdit ? '取消' : '返回'}
</Button>
</Form.Item>
</Form>
}
</Spin>
</Layout>
);
}
}
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {Table, Icon, Row, Col, Button, Form, Input, Cascader, Select} from 'antd';
import {
serialize,
formatDateTime,
productStatusToString,
productEnableCreateTrade,
UUID,
formItemLayout,
smallFormItemLayout,
footerFormSubmitLayout,
handleUpload,
filterFormItemLayout,
PRODUCT_STATUS,
filterFormItemStyle
} from '../../utils';
import {Link} from 'react-router';
import Layout from '../../components/Layout/Layout';
import MainHeader from '../../components/MainHeader/MainHeader';
@connect(state=>({
items: state.authority.items,
loading: state.authority.loading,
total: state.authority.total
}))
@Form.create()
export default class List extends Component {
constructor(props, context) {
super(props, context);
this.state = {
filterVisible: false
}
}
componentDidMount() {
this.fetchList(this.props.location.query);
};
fetchList(query) {
this.props.dispatch({
type: 'FETCH_AUTHORITY_LIST',
query
});
};
handleRowClick({name}) {
this.props.history.push('/admin/authorities/' + name);
}
handleFilterVisible() {
this.setState({
filterVisible: !this.state.filterVisible
});
}
handleFilterSubmit(e) {
e.preventDefault();
const formData = this.props.form.getFieldsValue();
if (formData.categoryId && formData.categoryId[1]) {
formData.categoryId = formData.categoryId[1];
}
const searchQuery = {...this.props.location.query, ...formData};
console.log(searchQuery);
this.props.history.replace(this.props.location.pathname + '?' + serialize(searchQuery));
this.fetchList(searchQuery);
}
handleResetFilterForm(e) {
e.preventDefault();
this.props.form.setFieldsValue({
id: undefined,
status: undefined,
categoryId: undefined,
title: undefined
});
}
render() {
const {
total, cates, items, loading,
form:{getFieldProps},
history:{replace},
location:{pathname, query}
} = this.props;
const columns = [
{
title: '名称',
dataIndex: 'name',
key: 'name',
width: 150,
}, {
title: '描述',
dataIndex: 'description',
key: 'description',
width: 100
}, {
title: '创建时间',
dataIndex: 'dateCreated',
key: 'dateCreated',
width: 150,
className: 'tac',
render: (dateCreated, record)=>(
<span>
{dateCreated && formatDateTime(dateCreated)}
</span>
)
}, {
title: '状态',
dataIndex: 'status',
key: 'status',
width: 80,
className: 'tac',
}, {
title: '操作',
key: 'operation',
width: 120,
className: 'tac',
render: (text, item)=>(
<span onClick={e=>{e.stopPropagation(); e.preventDefault();}}>
</span>
)
}
];
const pagination = {
total: total,
pageSize: parseInt(query.s, 10) || 10,
current: parseInt(query.p, 10) || 1,
showSizeChanger: true,
onShowSizeChange: (current, pageSize)=> {
console.log('Current: ', current, '; PageSize: ', pageSize);
query.p = current;
query.s = pageSize;
replace(pathname + '?' + serialize(query));
this.fetchList(query);
},
onChange: (current)=> {
console.log('Current: ', current);
query.p = current;
replace(pathname + '?' + serialize(query));
this.fetchList(query);
}
};
const operation = (
<div style={{textAlign:'right'}}>
<Button.Group>
<Button type="ghost" onClick={this.handleFilterVisible.bind(this)}>
<Icon type="filter"/>
</Button>
</Button.Group>
</div>
);
const header = (
<MainHeader breadcrumb={['权限管理', '权限资源列表']}
title="权限资源列表"
operation={operation}
>
{
this.state.filterVisible &&
<Form className="filterForm" inline onSubmit={this.handleFilterSubmit.bind(this)}>
<Form.Item label="ID">
<Input placeholder="请输入搜索ID" {...filterFormItemStyle} {...getFieldProps('id')}/>
</Form.Item>
<Form.Item label="类目">
{
cates &&
<Cascader options={cates} placeholder="请选产品类目" {...filterFormItemStyle}
{...getFieldProps('categoryId')}
/>
}
</Form.Item>
<Form.Item label="标题">
<Input placeholder="请输入搜索标题" {...filterFormItemStyle} {...getFieldProps('title')}/>
</Form.Item>
<Form.Item label="状态">
<Select placeholder="请选择状态" {...filterFormItemStyle} {...getFieldProps('status')}>
<Select.Option key="status-option-default"
value={null}>请选择</Select.Option>
{
ProductStatus.map((status, index)=>
<Select.Option key={status} data-value={status}
value={status}>{PRODUCT_STATUS[status]}</Select.Option>
)
}
</Select>
</Form.Item>
<Form.Item>
<Button.Group size="default">
<Button type="primary" size="default" htmlType="submit" loading={loading}><Icon
type="search"/>筛选</Button>
<Button type="ghost" size="default" htmlType="reset"
onClick={this.handleResetFilterForm.bind(this)}><Icon
type="cross-circle-o"/>清空</Button>
</Button.Group>
</Form.Item>
</Form>
}
</MainHeader>
);
return (
<Layout header={header}>
<Table className="list-table" columns={columns}
dataSource={Array.isArray(items)?items:[]}
loading={loading}
pagination={pagination}
scroll={{ y: window.innerHeight-(this.state.filterVisible? 203 :150) }}
onRowClick={this.handleRowClick.bind(this)}
/>
</Layout>
);
}
}
...@@ -34,7 +34,8 @@ export default class AddItem extends Component { ...@@ -34,7 +34,8 @@ export default class AddItem extends Component {
super(props, content); super(props, content);
this.state = { this.state = {
redirectName: undefined, redirectName: undefined,
sendName: undefined sendName: undefined,
channelType: 3
} }
} }
...@@ -43,7 +44,7 @@ export default class AddItem extends Component { ...@@ -43,7 +44,7 @@ export default class AddItem extends Component {
const data = this.props.form.getFieldsValue(); const data = this.props.form.getFieldsValue();
data.customMessage.redirectType = this.state.redirectName; data.customMessage.redirectType = this.state.redirectName;
data.customMessage.sendType = this.state.sendName; data.customMessage.sendType = this.state.sendName;
data.customMessage.channelType = this.state.channelType;
console.log(data); console.log(data);
this.props.dispatch({ this.props.dispatch({
...@@ -64,6 +65,13 @@ export default class AddItem extends Component { ...@@ -64,6 +65,13 @@ export default class AddItem extends Component {
const sendNamePlaceholder = {1: '无需填写', 2: '请输入一个产品ID', 3: '请输入接收消息的人员的手机号码'}; const sendNamePlaceholder = {1: '无需填写', 2: '请输入一个产品ID', 3: '请输入接收消息的人员的手机号码'};
// <Select placeholder="请选择" {...getFieldProps('customMessage.channelType')} >
// <Select.Option value="1">微信</Select.Option>
// <Select.Option value="3">APP</Select.Option>
// </Select>
const {channelType} = this.state;
return ( return (
<Layout header={header}> <Layout header={header}>
<Spin spinning={loading}> <Spin spinning={loading}>
...@@ -71,13 +79,34 @@ export default class AddItem extends Component { ...@@ -71,13 +79,34 @@ export default class AddItem extends Component {
<Form.Item label="推送渠道" {...formItemLayout}> <Form.Item label="推送渠道" {...formItemLayout}>
<Row> <Row>
<Col span="6"> <Col span="6">
<Select placeholder="请选择" {...getFieldProps('customMessage.channelType')} > <Radio.Group onChange={(e)=>{this.setState({channelType: e.target.value})}}
<Select.Option value="1">微信</Select.Option> value={this.state.channelType}>
<Select.Option value="3">APP</Select.Option> <Radio.Button key="b" value={3} checked={true}>APP</Radio.Button>
<Radio.Button key="a" value={1}>微信</Radio.Button>
</Radio.Group>
</Col>
</Row>
</Form.Item>
<Form.Item label="推送对象" {...formItemLayout}>
<Row>
<Col span="6">
<Select placeholder="请选择"
value={this.state.sendName}
onChange={value=>this.setState({sendName: value})}>
<Select.Option value="1">全局推送</Select.Option>
<Select.Option value="2">产品ID</Select.Option>
<Select.Option value="3">手动导入</Select.Option>
</Select> </Select>
</Col> </Col>
<Col span="18" style={{paddingLeft:'.5em'}}>
<Input placeholder={sendNamePlaceholder[this.state.sendName]}
{...getFieldProps('customMessage.send')} />
</Col>
</Row> </Row>
</Form.Item> </Form.Item>
{
channelType === 3 &&
<div>
<Form.Item label="消息跳转" {...formItemLayout}> <Form.Item label="消息跳转" {...formItemLayout}>
<Row> <Row>
<Col span="6"> <Col span="6">
...@@ -87,6 +116,7 @@ export default class AddItem extends Component { ...@@ -87,6 +116,7 @@ export default class AddItem extends Component {
<Select.Option value="0"></Select.Option> <Select.Option value="0"></Select.Option>
<Select.Option value="1">产品ID</Select.Option> <Select.Option value="1">产品ID</Select.Option>
<Select.Option value="2">URL</Select.Option> <Select.Option value="2">URL</Select.Option>
<Select.Option value="3">图片URL</Select.Option>
</Select> </Select>
</Col> </Col>
<Col span="18" style={{paddingLeft:'.5em'}}> <Col span="18" style={{paddingLeft:'.5em'}}>
...@@ -95,22 +125,11 @@ export default class AddItem extends Component { ...@@ -95,22 +125,11 @@ export default class AddItem extends Component {
</Col> </Col>
</Row> </Row>
</Form.Item> </Form.Item>
<Form.Item label="推送对象" {...formItemLayout}> <Form.Item label="弹窗提醒" {...formItemLayout}>
<Row> <Radio.Group {...getFieldProps('customMessage.isSend', {initialValue: false})}>
<Col span="6"> <Radio key="a" value={true}></Radio>
<Select placeholder="请选择" <Radio key="b" value={false} checked={true}></Radio>
value={this.state.sendName} </Radio.Group>
onChange={value=>this.setState({sendName: value})}>
<Select.Option value="1">全局推送</Select.Option>
<Select.Option value="2">产品ID</Select.Option>
<Select.Option value="3">手动导入</Select.Option>
</Select>
</Col>
<Col span="18" style={{paddingLeft:'.5em'}}>
<Input placeholder={sendNamePlaceholder[this.state.sendName]}
{...getFieldProps('customMessage.send')} />
</Col>
</Row>
</Form.Item> </Form.Item>
<Form.Item label="消息标题" {...formItemLayout}> <Form.Item label="消息标题" {...formItemLayout}>
<Input placeholder="消息标题" {...getFieldProps('customMessage.title')} /> <Input placeholder="消息标题" {...getFieldProps('customMessage.title')} />
...@@ -119,10 +138,16 @@ export default class AddItem extends Component { ...@@ -119,10 +138,16 @@ export default class AddItem extends Component {
<Input placeholder="消息摘要" type="textarea" <Input placeholder="消息摘要" type="textarea"
autosize={{minRows:3, maxRows:10}} {...getFieldProps('customMessage.abstracts')} /> autosize={{minRows:3, maxRows:10}} {...getFieldProps('customMessage.abstracts')} />
</Form.Item> </Form.Item>
<Form.Item label="消息正文" {...formItemLayout}> </div>
<Input placeholder="消息正文" type="textarea" }
<Form.Item label="消息正文" help={
<div dangerouslySetInnerHTML={{__html: channelType === 1 ? '微信跳转链接模板: &lt;a href="此处替换跳转链接"&gt;此处替换显示内容&lt;/a&gt;' : '最多50个字符' }} />
} {...formItemLayout}>
<Input placeholder="消息正文" type="textarea" maxLength="50"
autosize={{minRows:3, maxRows:10}} {...getFieldProps('customMessage.contents')} /> autosize={{minRows:3, maxRows:10}} {...getFieldProps('customMessage.contents')} />
</Form.Item> </Form.Item>
<Form.Item {...footerFormSubmitLayout} style={{marginTop:30}}> <Form.Item {...footerFormSubmitLayout} style={{marginTop:30}}>
<Button type="primary" htmlType="submit" loading={loading}><Icon type="save"/>立即推送</Button> <Button type="primary" htmlType="submit" loading={loading}><Icon type="save"/>立即推送</Button>
<Button onClick={e=>{e.preventDefault(); this.props.history.goBack();}} <Button onClick={e=>{e.preventDefault(); this.props.history.goBack();}}
......
...@@ -64,7 +64,7 @@ export default class EditItem extends Component { ...@@ -64,7 +64,7 @@ export default class EditItem extends Component {
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
this.setState({isEdit: nextProps.isEdit}); this.setState({isEdit: nextProps.isEdit});
} };
fetchCates() { fetchCates() {
this.props.dispatch({ this.props.dispatch({
......
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {
Row,
Col,
Form,
Input,
Button,
Checkbox,
Select,
message,
Tabs,
Cascader,
Radio,
Upload,
Icon,
Modal,
DatePicker,
Table,
Spin
} from 'antd';
import Layout from '../../components/Layout/Layout';
import MainHeader from '../../components/MainHeader/MainHeader';
import {
formItemLayout,
smallFormItemLayout,
footerFormSubmitLayout,
userStatusToString,
formatDateTime,
NULL
} from '../../utils';
import {
STATUS_LIST,
statusToString
} from './utils';
@connect(state=>({
loading: state.resource.loading,
item: state.resource.item,
isEdit: state.resource.isEdit
}))
@Form.create()
export default class EditItem extends Component {
constructor() {
super(...arguments);
this.state = {
isEdit: false
}
}
componentDidMount() {
this.fetchItem(this.props.params.id);
};
componentWillReceiveProps(nextProps) {
this.setState({isEdit: nextProps.isEdit});
};
fetchItem(id) {
this.props.dispatch({
type: 'FETCH_RESOURCE_ITEM',
id
});
};
handleSubmit(e) {
e.preventDefault();
let data = {};
data = this.props.form.getFieldsValue();
data.id = this.props.params.id;
if (isNaN(data.status)) {
STATUS_LIST.forEach(item=> {
if (item.text === data.status) {
data.status = item.value
}
})
}
console.log(data);
this.props.dispatch({
type: 'UPDATE_RESOURCE_ITEM',
item: data
});
}
render() {
const {item, loading, form:{getFieldProps}, location:{query}} = this.props;
const {isEdit} = this.state;
const operation = (
<div style={{textAlign:'right'}}>
<Button.Group>
<Button type="ghost"
onClick={
e=>{
e.preventDefault();
this.props.dispatch({
type: isEdit ? 'CANCEL_UPDATE_RESOURCE' : 'UPDATE_RESOURCE'
});
}
}>
<Icon type="edit"/>
</Button>
<Button type="ghost" onClick={e=>{
e.preventDefault();
isEdit ?
this.props.dispatch({
type: 'CANCEL_UPDATE_PRODUCT'
}) :
this.props.history.goBack();
}}>
<Icon type="rollback"/>
</Button>
</Button.Group>
</div>
);
const header = (
<MainHeader breadcrumb={['权限管理', '权限资源详情']}
title={'权限资源详情' + (isEdit ? ' - 编辑' : '')}
operation={operation}
/>
);
return (
<Layout header={header}>
<Spin spinning={loading}>
{
item &&
<Form className="main-form" horizontal>
<Form.Item label="分组" {...smallFormItemLayout}>
{
isEdit ?
<Input {...getFieldProps('category', {initialValue: item.category})} />
:
item.category || NULL
}
</Form.Item>
<Form.Item label="描述" {...smallFormItemLayout}>
{
isEdit ?
<Input {...getFieldProps('description', {initialValue: item.description})} />
:
item.description || NULL
}
</Form.Item>
<Form.Item label="状态" {...smallFormItemLayout}>
{
isEdit ?
<Select
placeholder="请选择状态" {...getFieldProps('status', {initialValue: statusToString(item.status)})} >
<Select.Option value="1">系统</Select.Option>
<Select.Option value="5">禁用</Select.Option>
<Select.Option value="9">启用</Select.Option>
</Select>
:
(typeof item.status !== 'undefined' ? statusToString(item.status) : NULL)
}
</Form.Item>
<Form.Item label="method" {...smallFormItemLayout}>
{ item.httpMethod }
</Form.Item>
<Form.Item label="url" {...smallFormItemLayout}>
{ item.url }
</Form.Item>
<Form.Item label="controllerName" {...smallFormItemLayout}>
{ item.controllerName }
</Form.Item>
<Form.Item label="actionName" {...smallFormItemLayout}>
{ item.actionName }
</Form.Item>
<Form.Item label="注册时间" {...smallFormItemLayout}>
{item.dateCreated && formatDateTime(item.dateCreated)}
</Form.Item>
<Form.Item {...footerFormSubmitLayout} style={{marginTop:30}}>
{
isEdit ?
<Button type="primary" onClick={this.handleSubmit.bind(this)}
loading={loading}>
<Icon type="save"/>保存
</Button>
:
<Button type="primary" onClick={e=>{
e.preventDefault();
this.props.dispatch({
type:'UPDATE_RESOURCE'
});
}}>
<Icon type="edit"/>编辑
</Button>
}
<Button onClick={e=>{
e.preventDefault();
isEdit ?
this.props.dispatch({
type: 'CANCEL_UPDATE_RESOURCE'
}) :
this.props.history.goBack();
}}
style={{marginLeft:'1em'}}>
<Icon type="rollback"/>
{isEdit ? '取消' : '返回'}
</Button>
</Form.Item>
</Form>
}
</Spin>
</Layout>
);
}
}
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {Table, Icon, Row, Col, Button, Form, Input, Cascader, Select} from 'antd';
import {
serialize,
formatDateTime,
productStatusToString,
productEnableCreateTrade,
UUID,
formItemLayout,
smallFormItemLayout,
footerFormSubmitLayout,
handleUpload,
filterFormItemLayout,
PRODUCT_STATUS,
filterFormItemStyle
} from '../../utils';
import {Link} from 'react-router';
import Layout from '../../components/Layout/Layout';
import MainHeader from '../../components/MainHeader/MainHeader';
import {
STATUS_LIST,
statusToString
} from './utils';
@connect(state=>({
items: state.resource.items,
loading: state.resource.loading,
total: state.resource.total
}))
@Form.create()
export default class List extends Component {
constructor(props, context) {
super(props, context);
this.state = {
filterVisible: false
}
}
componentDidMount() {
this.fetchList(this.props.location.query);
};
fetchList(query) {
this.props.dispatch({
type: 'FETCH_RESOURCE_LIST',
query
});
};
handleRowClick({id}) {
this.props.history.push('/admin/resources/' + id);
}
handleFilterVisible() {
this.setState({
filterVisible: !this.state.filterVisible
});
}
handleFilterSubmit(e) {
e.preventDefault();
const formData = this.props.form.getFieldsValue();
if (formData.categoryId && formData.categoryId[1]) {
formData.categoryId = formData.categoryId[1];
}
const searchQuery = {...this.props.location.query, ...formData};
console.log(searchQuery);
this.props.history.replace(this.props.location.pathname + '?' + serialize(searchQuery));
this.fetchList(searchQuery);
}
handleResetFilterForm(e) {
e.preventDefault();
this.props.form.setFieldsValue({
id: undefined,
status: undefined,
categoryId: undefined,
title: undefined
});
}
render() {
const {
total, cates, items, loading,
form:{getFieldProps},
history:{replace},
location:{pathname, query}
} = this.props;
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 60
}, {
title: 'category',
dataIndex: 'category',
key: 'category',
width: 80,
}, {
title: 'description',
dataIndex: 'description',
key: 'description',
width: 100
}, {
title: 'Method',
dataIndex: 'httpMethod',
key: 'httpMethod',
width: 60,
className: 'tac'
}, {
title: 'url',
dataIndex: 'url',
key: 'url',
width: 200
}, {
title: 'actionName',
dataIndex: 'actionName',
key: 'actionName',
width: 80,
className: 'tac'
}, {
title: 'controllerName',
dataIndex: 'controllerName',
key: 'controllerName',
width: 180,
}, {
title: '创建时间',
dataIndex: 'dateCreated',
key: 'dateCreated',
width: 150,
className: 'tac',
render: (dateCreated, record)=>(
<span>
{dateCreated && formatDateTime(dateCreated)}
</span>
)
}, {
title: 'status',
dataIndex: 'status',
key: 'status',
width: 80,
className: 'tac',
render: (status)=> (
<span>{statusToString(status)}</span>
)
}, {
title: '操作',
key: 'operation',
width: 120,
className: 'tac',
render: (text, item)=>(
<span onClick={e=>{e.stopPropagation(); e.preventDefault();}}>
</span>
)
}
];
const pagination = {
total: total,
pageSize: parseInt(query.s, 10) || 10,
current: parseInt(query.p, 10) || 1,
showSizeChanger: true,
onShowSizeChange: (current, pageSize)=> {
console.log('Current: ', current, '; PageSize: ', pageSize);
query.p = current;
query.s = pageSize;
replace(pathname + '?' + serialize(query));
this.fetchList(query);
},
onChange: (current)=> {
console.log('Current: ', current);
query.p = current;
replace(pathname + '?' + serialize(query));
this.fetchList(query);
}
};
const operation = (
<div style={{textAlign:'right'}}>
<Button.Group>
<Button type="ghost" onClick={this.handleFilterVisible.bind(this)}>
<Icon type="filter"/>
</Button>
</Button.Group>
</div>
);
const header = (
<MainHeader breadcrumb={['权限管理', '权限资源列表']}
title="权限资源列表"
operation={operation}
>
{
this.state.filterVisible &&
<Form className="filterForm" inline onSubmit={this.handleFilterSubmit.bind(this)}>
<Form.Item label="ID">
<Input placeholder="请输入搜索ID" {...filterFormItemStyle} {...getFieldProps('id')}/>
</Form.Item>
<Form.Item label="类目">
{
cates &&
<Cascader options={cates} placeholder="请选产品类目" {...filterFormItemStyle}
{...getFieldProps('categoryId')}
/>
}
</Form.Item>
<Form.Item label="标题">
<Input placeholder="请输入搜索标题" {...filterFormItemStyle} {...getFieldProps('title')}/>
</Form.Item>
<Form.Item label="状态">
<Select placeholder="请选择状态" {...filterFormItemStyle} {...getFieldProps('status')}>
<Select.Option key="status-option-default"
value={null}>请选择</Select.Option>
{
ProductStatus.map((status, index)=>
<Select.Option key={status} data-value={status}
value={status}>{PRODUCT_STATUS[status]}</Select.Option>
)
}
</Select>
</Form.Item>
<Form.Item>
<Button.Group size="default">
<Button type="primary" size="default" htmlType="submit" loading={loading}><Icon
type="search"/>筛选</Button>
<Button type="ghost" size="default" htmlType="reset"
onClick={this.handleResetFilterForm.bind(this)}><Icon
type="cross-circle-o"/>清空</Button>
</Button.Group>
</Form.Item>
</Form>
}
</MainHeader>
);
return (
<Layout header={header}>
<Table className="list-table" columns={columns}
dataSource={Array.isArray(items)?items:[]}
loading={loading}
pagination={pagination}
scroll={{ y: window.innerHeight-(this.state.filterVisible? 203 :150) }}
onRowClick={this.handleRowClick.bind(this)}
/>
</Layout>
);
}
}
export const STATUS = {
1: '系统',
5: '禁用',
9: '启用'
};
export function statusToString(status) {
return STATUS[status] || '未定义('+ status+')';
}
export const STATUS_LIST = Object.keys(STATUS).map(key=> {
return {value: key, text: STATUS[key]}
});
...@@ -24,4 +24,8 @@ export BaseUpload from './BaseFunction/BaseUpload'; ...@@ -24,4 +24,8 @@ export BaseUpload from './BaseFunction/BaseUpload';
export UsersAddItem from './Users/Additem'; export UsersAddItem from './Users/Additem';
export UsersList from './Users/List'; export UsersList from './Users/List';
export UsersItem from './Users/EditItem'; export UsersItem from './Users/EditItem';
export ResourceList from './Resource/List';
export ResourceItem from './Resource/EditItem';
export AuthorityList from './Authority/List';
export AuthorityItem from './Authority/EditItem';
import {handleActions} from 'redux-actions';
import {combineReducer} from 'redux';
const authority = handleActions({
['FETCH_AUTHORITY_LIST'](state) {
return {...state, loading: true};
},
['FETCH_AUTHORITY_LIST_SUCCESS'](state, action) {
return {...state, loading: false, items: action.items, total: action.total};
},
['FETCH_AUTHORITY_LIST_FAILED'](state, action) {
return {...state, err: action.err, loading: false};
},
['FETCH_AUTHORITY_ITEM'](state) {
return {...state, loading: true};
},
['FETCH_AUTHORITY_ITEM_SUCCESS'](state, action) {
return {...state, loading: false, item: action.item};
},
['FETCH_AUTHORITY_ITEM_FAILED'](state, action) {
return {...state, err: action.err, loading: false};
},
['UPDATE_AUTHORITY_START'](state){
return {...state, isEdit: true}
},
['UPDATE_AUTHORITY_END'](state){
return {...state, isEdit: false}
},
['UPDATE_AUTHORITY_ITEM'](state){
return {...state, loading: true}
},
['UPDATE_AUTHORITY_ITEM_SUCCESS'](state, action){
return {...state, loading: false, item: {...state.item, ...action.item}}
},
['UPDATE_AUTHORITY_ITEM_FAILED'](state, action){
return {...state, err: action.err, loading: false}
},
}, {
items: [],
loading: false,
});
export default authority;
import {handleActions} from 'redux-actions';
import {combineReducer} from 'redux';
const resources = handleActions({
['FETCH_RESOURCE_LIST'](state) {
return {...state, loading: true};
},
['FETCH_RESOURCE_LIST_SUCCESS'](state, action) {
return {...state, loading: false, items: action.items, total: action.total};
},
['FETCH_RESOURCE_LIST_FAILED'](state, action) {
return {...state, err: action.err, loading: false};
},
['FETCH_RESOURCE_ITEM'](state) {
return {...state, loading: true};
},
['FETCH_RESOURCE_ITEM_SUCCESS'](state, action) {
return {...state, loading: false, item: action.item};
},
['FETCH_RESOURCE_ITEM_FAILED'](state, action) {
return {...state, err: action.err, loading: false};
},
['UPDATE_RESOURCE_START'](state){
return {...state, isEdit: true}
},
['UPDATE_RESOURCE_END'](state){
return {...state, isEdit: false}
},
['UPDATE_RESOURCE_ITEM'](state){
return {...state, loading: true}
},
['UPDATE_RESOURCE_ITEM_SUCCESS'](state, action){
return {...state, loading: false, item: {...state.item, ...action.item}}
},
['UPDATE_RESOURCE_ITEM_FAILED'](state, action){
return {...state, err: action.err, loading: false}
},
}, {
items: [],
loading: false,
});
export default resources;
...@@ -26,7 +26,11 @@ import { ...@@ -26,7 +26,11 @@ import {
BaseUpload, BaseUpload,
UsersAddItem, UsersAddItem,
UsersList, UsersList,
UsersItem UsersItem,
ResourceList,
ResourceItem,
AuthorityList,
AuthorityItem
} from '../containers/index'; } from '../containers/index';
export default (store)=> { export default (store)=> {
...@@ -78,9 +82,17 @@ export default (store)=> { ...@@ -78,9 +82,17 @@ export default (store)=> {
<Route path="upload" component={BaseUpload}/> <Route path="upload" component={BaseUpload}/>
<Route path="admin"> <Route path="admin">
<Route path="users"> <Route path="users">
<IndexRoute component={UsersList} /> <IndexRoute component={UsersList}/>
<Route path="create" component={UsersAddItem} /> <Route path="create" component={UsersAddItem}/>
<Route path=":id" component={UsersItem} /> <Route path=":id" component={UsersItem}/>
</Route>
<Route path="resources">
<IndexRoute component={ResourceList}/>
<Route path=":id" component={ResourceItem}/>
</Route>
<Route path="authorities">
<IndexRoute component={AuthorityList}/>
<Route path=":id" component={AuthorityItem}/>
</Route> </Route>
</Route> </Route>
</Route> </Route>
......
import {takeLatest} from 'redux-saga';
import {take, call, put, fork, cancel, select} from 'redux-saga/effects';
import {message} from 'antd';
export default class RESTful {
constructor() {
}
index() {
const {api, id} = this;
return function*(query) {
try {
const result = yield call(api.index, query);
yield put({
type: 'FETCH_' + id + '_LIST_SUCCESS',
result
});
} catch (err) {
console.log(err);
message.error(err);
yield put({
type: 'FETCH_' + id + '_LIST_FAILED',
err,
});
}
}
}
show() {
const {api, id} = this;
return function*(id) {
try {
const result = yield call(api.show, id);
yield put({
type: 'FETCH_' + id + '_ITEM_SUCCESS',
result
});
} catch (err) {
console.log(err);
message.error(err);
yield put({
type: 'FETCH_' + id + '_ITEM_FAILED',
err
});
}
}
}
create() {
const {api, id} = this;
return function*(data) {
try {
const result = yield call(api.create, data);
yield put({
type: 'CREATE_' + id + '_ITEM_SUCCESS',
result
});
} catch (err) {
console.log(err);
message.error(err);
yield put({
type: 'CREATE_' + id + '_ITEM_FAILED',
err
});
}
}
}
}
import {takeLatest} from 'redux-saga';
import {take, call, put, fork, cancel, select, race} from 'redux-saga/effects';
import { fetchList, fetchItem, updateItem } from '../services/admin/authority';
import {message} from 'antd';
function* getList(query) {
try {
const {total, authorities} = yield call(fetchList, query);
yield put({
type: 'FETCH_AUTHORITY_LIST_SUCCESS',
total,
items: authorities
});
} catch (err) {
console.log(err);
message.error(err);
yield put({
type: 'FETCH_AUTHORITY_LIST_FAILED',
err,
});
}
}
function* watchList() {
while (true) {
const {query} = yield take('FETCH_AUTHORITY_LIST');
yield fork(getList, query);
}
}
function* getItem(id) {
try {
const item = yield call(fetchItem, id);
yield put({
type: 'FETCH_AUTHORITY_ITEM_SUCCESS',
item
});
} catch (err) {
console.log(err);
message.error(err);
yield put({
type: 'FETCH_AUTHORITY_ITEM_FAILED',
err
});
}
}
function* watchItem() {
while (true) {
const {id} = yield take('FETCH_AUTHORITY_ITEM');
yield fork(getItem, id);
}
}
function* editItem(data) {
try{
yield call(updateItem, data);
message.success('修改成功!');
yield put({
type: 'UPDATE_AUTHORITY_ITEM_SUCCESS',
item: data
});
}catch(err){
console.log(err);
message.error(err);
yield put({
type:'UPDATE_AUTHORITY_ITEM_FAILED',
err
});
}
}
function* watchEdit() {
while (true) {
yield take('UPDATE_AUTHORITY');
yield put({
type: 'UPDATE_AUTHORITY_START'
});
const {data} = yield race({
data: take('UPDATE_AUTHORITY_ITEM'),
canceled: take('CANCEL_UPDATE_AUTHORITY')
});
if (data && data.item) {
yield fork(editItem, data.item);
}
yield put({
type: 'UPDATE_AUTHORITY_END'
});
}
}
export default function*() {
yield fork(watchList);
yield fork(watchItem);
yield fork(watchEdit);
}
...@@ -3,7 +3,7 @@ import {fork} from 'redux-saga/effects'; ...@@ -3,7 +3,7 @@ import {fork} from 'redux-saga/effects';
// Use require.context to require sagas automatically // Use require.context to require sagas automatically
// Ref: https://webpack.github.io/docs/context.html // Ref: https://webpack.github.io/docs/context.html
const context = require.context('./', false, /\.js$/); const context = require.context('./', false, /\.js$/);
const keys = context.keys().filter(item => item !== './index.js' && item !== './SagaManager.js'); const keys = context.keys().filter(item => item !== './index.js' && item !== './SagaManager.js' && item !== './RESTful.js');
export default function* root() { export default function* root() {
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
......
import {takeLatest} from 'redux-saga';
import {take, call, put, fork, cancel, select, race} from 'redux-saga/effects';
import { fetchList, fetchItem, updateItem } from '../services/admin/resource';
import {message} from 'antd';
function* getList(query) {
try {
const {total, resources} = yield call(fetchList, query);
yield put({
type: 'FETCH_RESOURCE_LIST_SUCCESS',
total,
items: resources
});
} catch (err) {
console.log(err);
message.error(err);
yield put({
type: 'FETCH_RESOURCE_LIST_FAILED',
err,
});
}
}
function* watchList() {
while (true) {
const {query} = yield take('FETCH_RESOURCE_LIST');
yield fork(getList, query);
}
}
function* getItem(id) {
try {
const item = yield call(fetchItem, id);
yield put({
type: 'FETCH_RESOURCE_ITEM_SUCCESS',
item
});
} catch (err) {
console.log(err);
message.error(err);
yield put({
type: 'FETCH_RESOURCE_ITEM_FAILED',
err
});
}
}
function* watchItem() {
while (true) {
const {id} = yield take('FETCH_RESOURCE_ITEM');
yield fork(getItem, id);
}
}
function* editItem(data) {
try{
yield call(updateItem, data);
message.success('修改成功!');
yield put({
type: 'UPDATE_RESOURCE_ITEM_SUCCESS',
item: data
});
}catch(err){
console.log(err);
message.error(err);
yield put({
type:'UPDATE_RESOURCE_ITEM_FAILED',
err
});
}
}
function* watchEdit() {
while (true) {
yield take('UPDATE_RESOURCE');
yield put({
type: 'UPDATE_RESOURCE_START'
});
const {data} = yield race({
data: take('UPDATE_RESOURCE_ITEM'),
canceled: take('CANCEL_UPDATE_RESOURCE')
});
if (data && data.item) {
yield fork(editItem, data.item);
}
yield put({
type: 'UPDATE_RESOURCE_END'
});
}
}
export default function*() {
yield fork(watchList);
yield fork(watchItem);
yield fork(watchEdit);
}
import xFetch from '../xFetch';
import {serialize} from '../../utils';
export async function fetchList(query) {
return xFetch('/api/admin/authorities' + '?' +serialize(query));
}
export async function fetchItem(id) {
return xFetch('/api/admin/authorities/' + id);
}
export async function updateItem(item) {
return xFetch('/api/admin/authorities/'+item.id, {
method: 'PUT',
body: serialize(item)
});
}
import xFetch from '../xFetch';
import {serialize} from '../../utils';
export async function fetchList(query) {
return xFetch('/api/admin/resources' + '?' +serialize(query));
}
export async function fetchItem(id) {
return xFetch('/api/admin/resources/' + id);
}
export async function updateItem(item) {
return xFetch('/api/admin/resources/'+item.id, {
method: 'PUT',
body: serialize(item)
});
}
...@@ -8,8 +8,8 @@ export async function fetchItem(id) { ...@@ -8,8 +8,8 @@ export async function fetchItem(id) {
return xFetch('/api/remittance/audits/' + id); return xFetch('/api/remittance/audits/' + id);
} }
export async function pass(data) { export async function pass(data) {
return xFetch('/api/remittance/audits/op/' + data.id, { return xFetch('/api/remittance/audits/' + data.id, {
method: 'POST', method: 'PATCH',
body: serialize(data) body: serialize(data)
}); });
} }
......
...@@ -16,9 +16,13 @@ export async function createItem(data) { ...@@ -16,9 +16,13 @@ export async function createItem(data) {
} }
export async function settlementItem(id) { export async function settlementItem(id) {
return xFetch('/api/trade/commission/settlement/' + id); return xFetch('/api/trade/commission/' + id, {
method: 'PATCH'
});
} }
export async function establish(id) { export async function establish(id) {
return xFetch('/api/trade/success/' + id); return xFetch('/api/trade/' + id, {
method: 'PATCH'
});
} }
...@@ -9,8 +9,8 @@ export async function fetchItem(id) { ...@@ -9,8 +9,8 @@ export async function fetchItem(id) {
return xFetch('/api/withdraw/audits/' + id); return xFetch('/api/withdraw/audits/' + id);
} }
export async function pass(data) { export async function pass(data) {
return xFetch('/api/withdraw/audits/op/' + data.id, { return xFetch('/api/withdraw/audits/' + data.id, {
method: 'POST', method: 'PATCH',
body: serialize(data) body: serialize(data)
}); });
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
export const NULL = '_____?_____'; export const NULL = '_____?_____';
export function serialize(obj, prefix) { export function serialize(obj, prefix) {
if (obj) {
var str = []; var str = [];
Object.keys(obj).map(p=> { Object.keys(obj).map(p=> {
let v = obj[p]; let v = obj[p];
...@@ -21,6 +22,8 @@ export function serialize(obj, prefix) { ...@@ -21,6 +22,8 @@ export function serialize(obj, prefix) {
} }
}); });
return str.length ? str.join("&") : ''; return str.length ? str.join("&") : '';
}
return '';
} }
...@@ -55,7 +58,7 @@ export const PRODUCT_STATUS = { ...@@ -55,7 +58,7 @@ export const PRODUCT_STATUS = {
} }
export const productStatusToString = status => { export const productStatusToString = status => {
return PRODUCT_STATUS[status] || '未定义('+ status +')'; return PRODUCT_STATUS[status] || '未定义(' + status + ')';
}; };
export const productEnableCreateTrade = status => { export const productEnableCreateTrade = status => {
...@@ -68,7 +71,7 @@ export const USER_STATUS = { ...@@ -68,7 +71,7 @@ export const USER_STATUS = {
}; };
export const userStatusToString = status => { export const userStatusToString = status => {
return USER_STATUS[status] || '未定义('+ status +')'; return USER_STATUS[status] || '未定义(' + status + ')';
}; };
...@@ -95,7 +98,7 @@ export const tradeStatusToString = status => { ...@@ -95,7 +98,7 @@ export const tradeStatusToString = status => {
case 33: case 33:
return '交易关闭'; return '交易关闭';
default: default:
return '未定义('+ status +')'; return '未定义(' + status + ')';
} }
}; };
...@@ -222,23 +225,23 @@ export function remittanceAuditStatusToString(status) { ...@@ -222,23 +225,23 @@ export function remittanceAuditStatusToString(status) {
} }
} }
export function mobileSecrecy(mobile){ export function mobileSecrecy(mobile) {
return (mobile+'').replace(/^(\d{3})(\d{4})(\d{4})$/g, (b,c,d,e)=>(c+'****'+e)); return (mobile + '').replace(/^(\d{3})(\d{4})(\d{4})$/g, (b, c, d, e)=>(c + '****' + e));
} }
export function innerHTML(html, safe){ export function innerHTML(html, safe) {
return { return {
dangerouslySetInnerHTML: { dangerouslySetInnerHTML: {
__html: safe ? cr2br(htmlEncode(html)) :html __html: safe ? cr2br(htmlEncode(html)) : html
} }
} }
} }
export function htmlEncode(text){ export function htmlEncode(text) {
return (text+'').replace(/[\<\>\&]/gi, (m)=>{ return (text + '').replace(/[\<\>\&]/gi, (m)=> {
switch(m){ switch (m) {
case '<': case '<':
return '&lt;'; return '&lt;';
case '>': case '>':
...@@ -251,6 +254,18 @@ export function htmlEncode(text){ ...@@ -251,6 +254,18 @@ export function htmlEncode(text){
}); });
} }
export function cr2br(text){ export function cr2br(text) {
return (text+'').replace(/\n/g, '<br/>'); return (text + '').replace(/\n/g, '<br/>');
} }
export const ACTIONPERMISSION = {
index: 1 << 0,
create: 1 << 1,
save: 1 << 2,
show: 1 << 3,
edit: 1 << 4,
update: 1 << 5,
'delete': 1 << 6,
patch: 1 << 7
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment