Merge remote-tracking branch 'origin/master'

This commit is contained in:
YunaiV 2019-03-12 23:02:14 +08:00
commit 4fec581855
7 changed files with 390 additions and 96 deletions

View File

@ -1,11 +1,15 @@
import { message } from 'antd'; import { message } from 'antd';
import { buildTreeNode, findCheckedKeys } from '../../utils/tree.utils';
import { import {
addAdmin, addAdmin,
updateAdmin, updateAdmin,
updateAdminStatus, updateAdminStatus,
deleteAdmin, deleteAdmin,
queryAdmin, queryAdmin,
queryAdminRoleList,
adminRoleAssign,
} from '../../services/admin'; } from '../../services/admin';
import { arrayToStringParams } from '../../utils/request.qs';
export default { export default {
namespace: 'adminList', namespace: 'adminList',
@ -15,6 +19,10 @@ export default {
count: 0, count: 0,
pageNo: 0, pageNo: 0,
pageSize: 10, pageSize: 10,
roleList: [],
roleCheckedKeys: [],
roleAssignLoading: false,
}, },
effects: { effects: {
@ -78,6 +86,40 @@ export default {
}, },
}); });
}, },
*queryRoleList({ payload }, { call, put }) {
yield put({
type: 'changeRoleAssignLoading',
payload: true,
});
const response = yield call(queryAdminRoleList, payload);
const roleList = response.data;
const roleTreeData = buildTreeNode(roleList, 'name', 'id');
const roleCheckedKeys = findCheckedKeys(roleList);
yield put({
type: 'querySuccess',
payload: {
roleList: roleTreeData,
roleCheckedKeys,
},
});
yield put({
type: 'changeRoleAssignLoading',
payload: false,
});
},
*roleAssign({ payload }, { call }) {
const params = {
id: payload.id,
roleIds: arrayToStringParams(payload.roleIds),
};
const response = yield call(adminRoleAssign, params);
if (response.code === 0) {
message.info('操作成功!');
}
},
}, },
reducers: { reducers: {
@ -87,5 +129,17 @@ export default {
...payload, ...payload,
}; };
}, },
changeRoleCheckedKeys(state, { payload }) {
return {
...state,
roleCheckedKeys: payload,
};
},
changeRoleAssignLoading(state, { payload }) {
return {
...state,
roleAssignLoading: payload,
};
},
}, },
}; };

View File

@ -1,5 +1,6 @@
import { message } from 'antd'; import { message } from 'antd';
import { arrayToStringParams } from '../../utils/request.qs'; import { arrayToStringParams } from '../../utils/request.qs';
import { buildTreeNode, findAllNodes, findCheckedKeys } from '../../utils/tree.utils';
import { import {
addRole, addRole,
updateRole, updateRole,
@ -9,71 +10,6 @@ import {
roleAssignResource, roleAssignResource,
} from '../../services/admin'; } from '../../services/admin';
function buildTreeNode(nodes, titleKey, nodeKey) {
return nodes.map(item => {
const res = {};
if (item.children) {
res.children = buildTreeNode(item.children, titleKey, nodeKey);
}
res.title = `${item.id}-${item[titleKey]}`;
res.key = item[nodeKey];
return res;
});
}
function findNodes(id, nodes) {
const res = [];
for (let i = 0; i < nodes.length; i += 1) {
const node = nodes[i];
if (node.key === id) {
res.push(node.key);
break;
} else {
const childNodes = findNodes(id, node.children);
if (childNodes.length) {
res.push(node.key);
for (let j = 0; j < childNodes.length; j += 1) {
res.push(childNodes[j]);
}
break;
}
}
}
return res;
}
function findAllNodes(resourceIds, nodes) {
const findNodesArray = [];
for (let i = 0; i < resourceIds.length; i += 1) {
const findNodesData = findNodes(resourceIds[i], nodes);
if (findNodesData) {
for (let j = 0; j < findNodesData.length; j += 1) {
const jD = findNodesData[j];
if (findNodesArray.indexOf(jD) === -1) {
findNodesArray.push(jD);
}
}
}
}
return findNodesArray;
}
function findCheckedKeys(nodes) {
let res = [];
for (let i = 0; i < nodes.length; i += 1) {
const node = nodes[i];
if (node.children) {
const findChildrenNodes = findCheckedKeys(node.children);
if (findChildrenNodes) {
res = res.concat(findChildrenNodes);
}
} else if (node.assigned === true) {
res.push(node.id);
}
}
return res;
}
export default { export default {
namespace: 'roleList', namespace: 'roleList',

View File

@ -72,20 +72,87 @@ const filterMenuData = menuData => {
.filter(item => item); .filter(item => item);
}; };
/** // 用于生成uuid
* 递归构建服务端配置的菜单 function S4() {
* @param resultMenuData return ((1 + Math.random()) * 0x10000 || 0).toString(16).substring(1);
*/ }
const recursionBuildResultMenu = resultMenuData => { function guid() {
const res = {}; return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4();
for (let i = 0; i < resultMenuData.length; i += 1) { }
const menuItem = resultMenuData[i];
// 存在子节点 const findRootMenu = (antDataMenus, rootAntDataMenu, requestDataMenu) => {
res[menuItem.handler] = { let res;
...menuItem, for (let i = 0; i < antDataMenus.length; i += 1) {
const antDataMenu = antDataMenus[i];
if (antDataMenu.path === requestDataMenu.handler) {
res = rootAntDataMenu;
break;
}
if (antDataMenu.children) {
res = findRootMenu(antDataMenu.children, antDataMenu, requestDataMenu);
break;
}
}
return res;
};
const buildTreeMenu = (antMenuData, moveChildrenMenusData, requestDataMenus) => {
return requestDataMenus.map(item => {
if (!item.handler) {
// root 节点
const uuid = `sms${guid()}`;
const res = {
icon: 'user',
name: item.displayName,
path: uuid,
};
// 子节点
if (item.children) {
// 通过子节点找到对于的父节点,设置 path没有则是 uuid
const rootMenu = findRootMenu(antMenuData, {}, item.children[0]);
if (rootMenu) {
res.path = rootMenu.path;
}
// 开始递归构建数据结构
const childrenMenus = buildTreeMenu(antMenuData, moveChildrenMenusData, item.children);
res.children = childrenMenus;
}
return res;
}
// moveChildrenMenusData 是一个 map对比 url 地址是否存在,不存在就给一个 404 的页面
const handleMapperData = moveChildrenMenusData[item.handler];
if (handleMapperData) {
return {
...handleMapperData,
icon: 'user',
name: item.displayName,
path: item.handler,
};
}
// 没有就返回404页面
return moveChildrenMenusData['/exception/404'];
});
};
const moveChildrenMenus = antDataMenus => {
let res = {};
for (let i = 0; i < antDataMenus.length; i += 1) {
const antDataMenu = antDataMenus[i];
res[antDataMenu.path] = {
...res,
...antDataMenu,
}; };
if (menuItem.children) {
res[menuItem.handler].children = recursionBuildResultMenu(menuItem.children); if (antDataMenu.children) {
const childrenMenus = moveChildrenMenus(antDataMenu.children);
res = {
...res,
...childrenMenus,
};
} }
} }
return res; return res;
@ -126,21 +193,15 @@ export default {
*getMenuData({ payload }, { put, call }) { *getMenuData({ payload }, { put, call }) {
const { data } = yield call(getAdminMenus); const { data } = yield call(getAdminMenus);
const { routes, authority } = payload; const { routes, authority } = payload;
// authority 已经不适用 // authority 已经不适用
const antMenuData = filterMenuData(memoizeOneFormatter(routes, authority)); const antMenuData = filterMenuData(memoizeOneFormatter(routes, authority));
let menuData = antMenuData; let menuData = antMenuData;
const resultMenuData = data; // const resultMenuData = data;
if (data !== 'all') { if (data !== 'all') {
// 处理后台数据结构 const moveChildrenMenusData = moveChildrenMenus(antMenuData);
const buildResultMenu = recursionBuildResultMenu(resultMenuData); const buildTreeMenuData = buildTreeMenu(antMenuData, moveChildrenMenusData, data);
// 过滤没有权限的菜单 menuData = buildTreeMenuData;
menuData = antMenuData.filter(item => {
if (buildResultMenu[item.path]) {
return item;
}
return false;
});
} }
// 生成 menu 和 router mapping // 生成 menu 和 router mapping

View File

@ -2,14 +2,13 @@
import React, { PureComponent, Fragment } from 'react'; import React, { PureComponent, Fragment } from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import moment from 'moment'; import { Card, Form, Input, Button, Modal, message, Table, Divider, Tree, Spin } from 'antd';
import { Card, Form, Input, Select, Button, Modal, message, Table, Divider } from 'antd';
import PageHeaderWrapper from '@/components/PageHeaderWrapper'; import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import styles from './AdminList.less'; import styles from './AdminList.less';
const FormItem = Form.Item; const FormItem = Form.Item;
const { Option } = Select; const { TreeNode } = Tree;
const status = ['未知', '正常', '禁用']; const status = ['未知', '正常', '禁用'];
// 添加 form 表单 // 添加 form 表单
@ -64,6 +63,78 @@ const CreateForm = Form.create()(props => {
); );
}); });
// 角色分配
const RoleAssignModal = Form.create()(props => {
const {
modalVisible,
form,
handleOk,
handleModalVisible,
treeData,
checkedKeys,
loading,
handleCheckBoxClick,
} = props;
const renderTreeNodes = data => {
return data.map(item => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode title={item.title} key={item.key} dataRef={item} />;
});
};
const renderModalContent = treeData => {
const RenderTreeNodes = renderTreeNodes(treeData);
if (RenderTreeNodes) {
return (
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="角色">
{form.getFieldDecorator('name', {})(
<Tree
defaultExpandAll={true}
checkable={true}
multiple={true}
checkedKeys={checkedKeys}
onCheck={handleCheckBoxClick}
>
{renderTreeNodes(treeData)}
</Tree>
)}
</FormItem>
);
} else {
return null;
}
};
const okHandle = () => {
form.validateFields((err, fieldsValue) => {
if (err) return;
form.resetFields();
handleOk({
fields: fieldsValue,
});
});
};
return (
<Modal
destroyOnClose
title="分配角色"
visible={modalVisible}
onOk={okHandle}
onCancel={() => handleModalVisible()}
>
<Spin spinning={loading}>{renderModalContent(treeData)}</Spin>
</Modal>
);
});
@connect(({ adminList, loading }) => ({ @connect(({ adminList, loading }) => ({
list: adminList.list, list: adminList.list,
data: adminList, data: adminList,
@ -75,6 +146,9 @@ class ResourceList extends PureComponent {
modalVisible: false, modalVisible: false,
modalType: 'add', //add update modalType: 'add', //add update
initValues: {}, initValues: {},
modalRoleVisible: false,
modalRoleRow: {},
}; };
componentDidMount() { componentDidMount() {
@ -160,9 +234,87 @@ class ResourceList extends PureComponent {
}); });
} }
handleDelete(row) {
const { dispatch, data } = this.props;
const queryParams = {
pageNo: data.pageNo,
pageSize: data.pageSize,
};
Modal.confirm({
title: `确认删除?`,
content: `${row.username}`,
onOk() {
dispatch({
type: 'adminList/delete',
payload: {
body: {
id: row.id,
},
queryParams,
},
});
},
onCancel() {},
});
}
handleRoleAssign = row => {
const { dispatch } = this.props;
this.setState({
modalRoleVisible: !!true,
modalRoleRow: row,
});
dispatch({
type: 'adminList/queryRoleList',
payload: {
id: row.id,
},
});
};
handleRoleAssignCheckBoxClick = checkedKeys => {
const { dispatch } = this.props;
const newCheckedKeys = checkedKeys.map(item => {
return parseInt(item);
});
dispatch({
type: 'adminList/changeRoleCheckedKeys',
payload: newCheckedKeys,
});
};
handleRoleAssignOK = () => {
const { dispatch, data } = this.props;
const { modalRoleRow } = this.state;
dispatch({
type: 'adminList/roleAssign',
payload: {
id: modalRoleRow.id,
roleIds: data.roleCheckedKeys,
},
});
this.handleRoleAssignModalVisibleClose(false);
};
handleRoleAssignModalVisibleClose = fag => {
this.setState({
modalRoleVisible: !!fag,
});
};
render() { render() {
const { list } = this.props; const { list, data } = this.props;
const { modalVisible, modalType, initValues, defaultExpandAllRows } = this.state; const { roleList, roleCheckedKeys, roleAssignLoading } = data;
const {
modalVisible,
modalType,
initValues,
defaultExpandAllRows,
modalRoleVisible,
} = this.state;
const parentMethods = { const parentMethods = {
handleAdd: this.handleAdd, handleAdd: this.handleAdd,
handleModalVisible: this.handleModalVisible, handleModalVisible: this.handleModalVisible,
@ -194,12 +346,15 @@ class ResourceList extends PureComponent {
}, },
{ {
title: '操作', title: '操作',
width: 300,
render: (text, record) => { render: (text, record) => {
const statusText = record.status === 1 ? '确认禁用' : '取消禁用'; const statusText = record.status === 1 ? '确认禁用' : '取消禁用';
return ( return (
<Fragment> <Fragment>
<a onClick={() => this.handleModalVisible(true, 'update', record)}>更新</a> <a onClick={() => this.handleModalVisible(true, 'update', record)}>更新</a>
<Divider type="vertical" /> <Divider type="vertical" />
<a onClick={() => this.handleRoleAssign(record)}>角色分配</a>
<Divider type="vertical" />
<a className={styles.tableDelete} onClick={() => this.handleStatus(record)}> <a className={styles.tableDelete} onClick={() => this.handleStatus(record)}>
{statusText} {statusText}
</a> </a>
@ -235,6 +390,16 @@ class ResourceList extends PureComponent {
/> />
</Card> </Card>
<CreateForm {...parentMethods} modalVisible={modalVisible} /> <CreateForm {...parentMethods} modalVisible={modalVisible} />
<RoleAssignModal
loading={roleAssignLoading}
treeData={roleList}
checkedKeys={roleCheckedKeys}
handleOk={this.handleRoleAssignOK}
modalVisible={modalRoleVisible}
handleCheckBoxClick={this.handleRoleAssignCheckBoxClick}
handleModalVisible={() => this.handleRoleAssignModalVisibleClose(false)}
/>
</PageHeaderWrapper> </PageHeaderWrapper>
); );
} }

View File

@ -21,7 +21,6 @@ import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import styles from './RoleList.less'; import styles from './RoleList.less';
const FormItem = Form.Item; const FormItem = Form.Item;
const { Option } = Select;
const { TreeNode } = Tree; const { TreeNode } = Tree;
// 添加 form 表单 // 添加 form 表单
@ -65,7 +64,7 @@ const CreateForm = Form.create()(props => {
); );
}); });
// 添加 form 表单 // 角色分配
const AssignModal = Form.create()(props => { const AssignModal = Form.create()(props => {
const { const {
modalVisible, modalVisible,

View File

@ -43,6 +43,18 @@ export async function deleteAdmin(params) {
}); });
} }
export async function queryAdminRoleList(params) {
return request(`/admin-api/admins/admin/role_list?${stringify(params)}`, {
method: 'GET',
});
}
export async function adminRoleAssign(params) {
return request(`/admin-api/admins/admin/assign_role?${stringify(params)}`, {
method: 'POST',
});
}
// resource // resource
export async function addResource(params) { export async function addResource(params) {

View File

@ -0,0 +1,67 @@
// tree 工具
export function buildTreeNode(nodes, titleKey, nodeKey) {
return nodes.map(item => {
const res = {};
if (item.children) {
res.children = buildTreeNode(item.children, titleKey, nodeKey);
}
res.title = `${item.id}-${item[titleKey]}`;
res.key = item[nodeKey];
return res;
});
}
// @primary
function findNodes(id, nodes) {
const res = [];
for (let i = 0; i < nodes.length; i += 1) {
const node = nodes[i];
if (node.key === id) {
res.push(node.key);
break;
} else {
const childNodes = findNodes(id, node.children);
if (childNodes.length) {
res.push(node.key);
for (let j = 0; j < childNodes.length; j += 1) {
res.push(childNodes[j]);
}
break;
}
}
}
return res;
}
export function findAllNodes(resourceIds, nodes) {
const findNodesArray = [];
for (let i = 0; i < resourceIds.length; i += 1) {
const findNodesData = findNodes(resourceIds[i], nodes);
if (findNodesData) {
for (let j = 0; j < findNodesData.length; j += 1) {
const jD = findNodesData[j];
if (findNodesArray.indexOf(jD) === -1) {
findNodesArray.push(jD);
}
}
}
}
return findNodesArray;
}
export function findCheckedKeys(nodes) {
let res = [];
for (let i = 0; i < nodes.length; i += 1) {
const node = nodes[i];
if (node.children) {
const findChildrenNodes = findCheckedKeys(node.children);
if (findChildrenNodes) {
res = res.concat(findChildrenNodes);
}
} else if (node.assigned === true) {
res.push(node.id);
}
}
return res;
}