Commit 688aa87b authored by superman's avatar superman

修改了消息推送

parent 68fd4884
This diff is collapsed.
......@@ -57,4 +57,4 @@
"start": "dora --plugins \"proxy,webpack,webpack-hmr,livereload?enableJs=false&injectHost=127.0.0.1,browser-history?index=/src/entries/index.html\"",
"test": "atool-test-mocha ./src/**/__tests__/*-test.js"
}
}
\ No newline at end of file
}
......@@ -2,6 +2,6 @@
// - https://github.com/dora-js/dora-plugin-proxy#规则定义
module.exports = {
// '/api/*': 'http://react.yanky.cn/',
'/api/*': 'http://192.168.1.126:8080/'
'/api/*': 'http://react.yanky.cn/',
// '/api/*': 'http://192.168.1.126:8080/'
};
......@@ -25,6 +25,7 @@ export default class App extends Component {
const styles = require('./App.less');
const menu = [
{
id:'business',
title: '业务管理',
icon: 'mail',
items: [
......@@ -79,6 +80,7 @@ export default class App extends Component {
}
]
}, {
id:'base',
title: '基础功能',
icon: 'folder',
items: [
......@@ -94,6 +96,7 @@ export default class App extends Component {
}
]
}, {
id:'admin',
title: '管理员',
icon: 'folder',
items: [
......@@ -110,13 +113,29 @@ export default class App extends Component {
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 openKey = '';
return (
<div className={styles.content}>
<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 {
super(props, content);
this.state = {
redirectName: undefined,
sendName: undefined
sendName: undefined,
channelType: 3
}
}
......@@ -43,7 +44,7 @@ export default class AddItem extends Component {
const data = this.props.form.getFieldsValue();
data.customMessage.redirectType = this.state.redirectName;
data.customMessage.sendType = this.state.sendName;
data.customMessage.channelType = this.state.channelType;
console.log(data);
this.props.dispatch({
......@@ -64,6 +65,13 @@ export default class AddItem extends Component {
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 (
<Layout header={header}>
<Spin spinning={loading}>
......@@ -71,27 +79,11 @@ export default class AddItem extends Component {
<Form.Item label="推送渠道" {...formItemLayout}>
<Row>
<Col span="6">
<Select placeholder="请选择" {...getFieldProps('customMessage.channelType')} >
<Select.Option value="1">微信</Select.Option>
<Select.Option value="3">APP</Select.Option>
</Select>
</Col>
</Row>
</Form.Item>
<Form.Item label="消息跳转" {...formItemLayout}>
<Row>
<Col span="6">
<Select placeholder="请选择"
value={this.state.redirectName}
onChange={value=>this.setState({redirectName: value})}>
<Select.Option value="0"></Select.Option>
<Select.Option value="1">产品ID</Select.Option>
<Select.Option value="2">URL</Select.Option>
</Select>
</Col>
<Col span="18" style={{paddingLeft:'.5em'}}>
<Input placeholder={redirectNamePlaceholder[this.state.redirectName]}
{...getFieldProps('customMessage.redirect')} />
<Radio.Group onChange={(e)=>{this.setState({channelType: e.target.value})}}
value={this.state.channelType}>
<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>
......@@ -112,17 +104,50 @@ export default class AddItem extends Component {
</Col>
</Row>
</Form.Item>
<Form.Item label="消息标题" {...formItemLayout}>
<Input placeholder="消息标题" {...getFieldProps('customMessage.title')} />
</Form.Item>
<Form.Item label="消息摘要" {...formItemLayout}>
<Input placeholder="消息摘要" type="textarea"
autosize={{minRows:3, maxRows:10}} {...getFieldProps('customMessage.abstracts')} />
</Form.Item>
<Form.Item label="消息正文" {...formItemLayout}>
<Input placeholder="消息正文" type="textarea"
{
channelType === 3 &&
<div>
<Form.Item label="消息跳转" {...formItemLayout}>
<Row>
<Col span="6">
<Select placeholder="请选择"
value={this.state.redirectName}
onChange={value=>this.setState({redirectName: value})}>
<Select.Option value="0"></Select.Option>
<Select.Option value="1">产品ID</Select.Option>
<Select.Option value="2">URL</Select.Option>
<Select.Option value="3">图片URL</Select.Option>
</Select>
</Col>
<Col span="18" style={{paddingLeft:'.5em'}}>
<Input placeholder={redirectNamePlaceholder[this.state.redirectName]}
{...getFieldProps('customMessage.redirect')} />
</Col>
</Row>
</Form.Item>
<Form.Item label="弹窗提醒" {...formItemLayout}>
<Radio.Group {...getFieldProps('customMessage.isSend', {initialValue: false})}>
<Radio key="a" value={true}></Radio>
<Radio key="b" value={false} checked={true}></Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="消息标题" {...formItemLayout}>
<Input placeholder="消息标题" {...getFieldProps('customMessage.title')} />
</Form.Item>
<Form.Item label="消息摘要" {...formItemLayout}>
<Input placeholder="消息摘要" type="textarea"
autosize={{minRows:3, maxRows:10}} {...getFieldProps('customMessage.abstracts')} />
</Form.Item>
</div>
}
<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')} />
</Form.Item>
<Form.Item {...footerFormSubmitLayout} style={{marginTop:30}}>
<Button type="primary" htmlType="submit" loading={loading}><Icon type="save"/>立即推送</Button>
<Button onClick={e=>{e.preventDefault(); this.props.history.goBack();}}
......
......@@ -64,7 +64,7 @@ export default class EditItem extends Component {
componentWillReceiveProps(nextProps) {
this.setState({isEdit: nextProps.isEdit});
}
};
fetchCates() {
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';
export UsersAddItem from './Users/Additem';
export UsersList from './Users/List';
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 {
BaseUpload,
UsersAddItem,
UsersList,
UsersItem
UsersItem,
ResourceList,
ResourceItem,
AuthorityList,
AuthorityItem
} from '../containers/index';
export default (store)=> {
......@@ -78,9 +82,17 @@ export default (store)=> {
<Route path="upload" component={BaseUpload}/>
<Route path="admin">
<Route path="users">
<IndexRoute component={UsersList} />
<Route path="create" component={UsersAddItem} />
<Route path=":id" component={UsersItem} />
<IndexRoute component={UsersList}/>
<Route path="create" component={UsersAddItem}/>
<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>
......
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';
// Use require.context to require sagas automatically
// Ref: https://webpack.github.io/docs/context.html
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() {
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) {
return xFetch('/api/remittance/audits/' + id);
}
export async function pass(data) {
return xFetch('/api/remittance/audits/op/' + data.id, {
method: 'POST',
return xFetch('/api/remittance/audits/' + data.id, {
method: 'PATCH',
body: serialize(data)
});
}
......
......@@ -16,9 +16,13 @@ export async function createItem(data) {
}
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) {
return xFetch('/api/trade/success/' + id);
return xFetch('/api/trade/' + id, {
method: 'PATCH'
});
}
......@@ -9,8 +9,8 @@ export async function fetchItem(id) {
return xFetch('/api/withdraw/audits/' + id);
}
export async function pass(data) {
return xFetch('/api/withdraw/audits/op/' + data.id, {
method: 'POST',
return xFetch('/api/withdraw/audits/' + data.id, {
method: 'PATCH',
body: serialize(data)
});
}
......@@ -5,22 +5,25 @@
export const NULL = '_____?_____';
export function serialize(obj, prefix) {
var str = [];
Object.keys(obj).map(p=> {
let v = obj[p];
if (p && typeof v !== 'undefined') {
let k = prefix ? prefix + "." + p : p;
if (typeof v == 'object') {
v = serialize(v, k);
if (v) {
str.push(v);
if (obj) {
var str = [];
Object.keys(obj).map(p=> {
let v = obj[p];
if (p && typeof v !== 'undefined') {
let k = prefix ? prefix + "." + p : p;
if (typeof v == 'object') {
v = serialize(v, k);
if (v) {
str.push(v);
}
} else {
str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v));
}
} else {
str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v));
}
}
});
return str.length ? str.join("&") : '';
});
return str.length ? str.join("&") : '';
}
return '';
}
......@@ -55,7 +58,7 @@ export const PRODUCT_STATUS = {
}
export const productStatusToString = status => {
return PRODUCT_STATUS[status] || '未定义('+ status +')';
return PRODUCT_STATUS[status] || '未定义(' + status + ')';
};
export const productEnableCreateTrade = status => {
......@@ -68,7 +71,7 @@ export const USER_STATUS = {
};
export const userStatusToString = status => {
return USER_STATUS[status] || '未定义('+ status +')';
return USER_STATUS[status] || '未定义(' + status + ')';
};
......@@ -95,7 +98,7 @@ export const tradeStatusToString = status => {
case 33:
return '交易关闭';
default:
return '未定义('+ status +')';
return '未定义(' + status + ')';
}
};
......@@ -222,23 +225,23 @@ export function remittanceAuditStatusToString(status) {
}
}
export function mobileSecrecy(mobile){
return (mobile+'').replace(/^(\d{3})(\d{4})(\d{4})$/g, (b,c,d,e)=>(c+'****'+e));
export function mobileSecrecy(mobile) {
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 {
dangerouslySetInnerHTML: {
__html: safe ? cr2br(htmlEncode(html)) :html
__html: safe ? cr2br(htmlEncode(html)) : html
}
}
}
export function htmlEncode(text){
return (text+'').replace(/[\<\>\&]/gi, (m)=>{
switch(m){
export function htmlEncode(text) {
return (text + '').replace(/[\<\>\&]/gi, (m)=> {
switch (m) {
case '<':
return '&lt;';
case '>':
......@@ -251,6 +254,18 @@ export function htmlEncode(text){
});
}
export function cr2br(text){
return (text+'').replace(/\n/g, '<br/>');
export function cr2br(text) {
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