| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- /**
- * 权限管理
- */
- import React from 'react'
- import { Tabs, Layout, Row, Col, Input, Menu, Table, Checkbox, message, Button, Icon } from 'antd'
- import { connect } from 'dva'
- import Loading from '../common/loading/index'
- import './index.less'
- const { Sider, Header, Content } = Layout
- const { Search } = Input
- const { TabPane } = Tabs
- class Authority extends React.Component {
- componentDidMount() {
- const { dispatch } = this.props;
- dispatch({ type: 'userGroup/fetchList' });
- dispatch({ type: 'user/fetchList' });
- dispatch({ type: 'dashboard/remoteMenuTree' });
- }
- checkIndeterminate = (treeList) => {
- const { authority } = this.props;
- const { dashboardList, dashboardFilterLabel } = authority;
- let arr = treeList && treeList.length > 0 ? [ ...treeList ] : null;
- for(let i = (arr ? (arr.length - 1) : 0); arr && i >= 0; i--) {
- let l = arr[i];
- if(l.type === 'menu') {
- l.children = this.checkIndeterminate(l.children);
- let checkedCount = l.children ? l.children.filter(c => c.type === 'dashboard').map(c => c.checked).filter(c => !!c).length : 0;
- l.indeterminate = l.children ? (checkedCount > 0 && checkedCount < l.children.filter(c => c.type === 'dashboard').length) : false;
- l.checked = l.children ? checkedCount !== 0 && checkedCount === l.children.filter(c => c.type === 'dashboard').length : false;
- if((!l.children || l.children.length === 0) && !!dashboardFilterLabel && l.name.toLowerCase().indexOf(dashboardFilterLabel.toLowerCase()) === -1) {
- arr.splice(i, 1);
- }
- }else {
- l.children = null;
- l.indeterminate = false;
- l.checked = dashboardList.findIndex(d => d === l.code) > -1;
- if(!!dashboardFilterLabel && l.name.toLowerCase().indexOf(dashboardFilterLabel.toLowerCase()) === -1) {
- arr.splice(i, 1);
- }
- }
- }
- return arr;
- }
- changeTab = (key) => {
- const { dispatch } = this.props;
- dispatch({ type: 'authority/setFields', fields: [
- { name: 'tabActiveKey', value: key },
- { name: 'menuSelectedKeys', value: [] },
- { name: 'dashboardList', value: [] },
- ] });
- }
- render() {
- const { dashboard, authority, userGroup, user, dispatch } = this.props;
- const { panelLoading, menuSelectedKeys, tabActiveKey, groupFilterLabel, userFilterLabel, dashboardFilterLabel, groupLimit, userLimit } = authority;
-
- const dashboardTreeList = this.checkIndeterminate(JSON.parse(JSON.stringify(dashboard.menuTree)));
- return <Layout className='layout-authority'>
- <Loading visible={panelLoading} />
- <Sider>
- <Tabs
- className='tabs-authority'
- type='card'
- tabPosition='top'
- defaultActiveKey="userGroup"
- activeKey={tabActiveKey}
- onChange={this.changeTab}
- >
- <TabPane tab="用户组" key="userGroup" >
- <MenuList
- refName='group'
- selectedKeys={menuSelectedKeys}
- list={userGroup.list}
- filterLabel={groupFilterLabel}
- filterLabelChange={(val) => {
- dispatch({ type: 'authority/setFields', fields: [
- { name: 'groupLimit', value: 30 },
- { name: 'groupFilterLabel', value: val },
- ] })
- }}
- displayField='name'
- valueField='code'
- onItemClick={(item) => {
- dispatch({ type: 'authority/batchActions', actions: [
- { type: 'setFields', fields: [
- { name: 'menuSelectedKeys', value: [item.code] },
- ] },
- { type: 'fetchDashboardTree', dtype: '0', code: item.code }
- ] });
- }}
- limit={groupLimit}
- onScrollToBottom={() => {
- window.clearTimeout(this.groupLimitKey);
- this.groupLimitKey = window.setTimeout(() => {
- dispatch({ type: 'authority/setFields', fields: [
- { name: 'groupLimit', value: (groupLimit + 10 > userGroup.list.length ? userGroup.list.length : groupLimit + 10) },
- ] })
- }, 500);
- }}
- />
- </TabPane>
- <TabPane tab="用户" key="user">
- <MenuList
- refName='user'
- selectedKeys={menuSelectedKeys}
- list={user.list}
- filterLabel={userFilterLabel}
- filterLabelChange={(val) => {
- dispatch({ type: 'authority/setFields', fields: [
- { name: 'userLimit', value: 30 },
- { name: 'userFilterLabel', value: val },
- ] })
- }}
- displayField='fullName'
- valueField='code'
- onItemClick={(item) => {
- dispatch({ type: 'authority/batchActions', actions: [
- { type: 'setFields', fields: [ { name: 'menuSelectedKeys', value: [item.code] }, ]},
- { type: 'fetchDashboardTree', dtype: '1', code: item.code }
- ] });
- }}
- limit={userLimit}
- onScrollToBottom={() => {
- window.clearTimeout(this.userLimitKey);
- this.userLimitKey = window.setTimeout(() => {
- dispatch({ type: 'authority/setFields', fields: [
- { name: 'userLimit', value: (userLimit + 10 > user.list.length ? user.list.length : userLimit + 10) },
- ] })
- }, 500);
- }}
- />
- </TabPane>
- </Tabs>
- </Sider>
- <Content>
- <Header>
- <Row className='search-bar' type='flex' justify='end'>
- <Col className='search'>
- <Button className='btn-refresh' onClick={() => {
- dispatch({ type: 'authority/setField', name: 'panelLoading', value: true });
- dispatch({ type: 'userGroup/fetchList', mandatory: true });
- dispatch({ type: 'user/fetchList', mandatory: true });
- dispatch({ type: 'dashboard/remoteMenuTree', mandatory: true }).then(() => {
- dispatch({ type: 'authority/setField', name: 'panelLoading', value: false });
- });
- }}>
- <Icon type='sync'/>
- </Button>
- <Search
- placehodler='请输入关键字'
- value={dashboardFilterLabel}
- onChange={e => {
- let value = e.target.value;
- dispatch({ type: 'authority/setFields', fields: [
- { name: 'dashboardFilterLabel', value },
- ] })
- }}
- />
- </Col>
- </Row>
- </Header>
- <DashBoardTree filterLabel={dashboardFilterLabel} dataSource={dashboardTreeList} onRowCheckChange={(record, checked) => {
- if(!!menuSelectedKeys[0]) {
- if(record.type === 'menu') {
- let dashboardCodes = record.children.filter(c => c.type === 'dashboard').map(c => c.code)
- dispatch({ type: 'authority/' + (checked ? 'addAll': 'removeAll'), dtype: tabActiveKey === 'user' ? 1 : 0, code: menuSelectedKeys[0], dashboardCodes });
- }else {
- dispatch({ type: 'authority/' + (checked ? 'add': 'remove'), dtype: tabActiveKey === 'user' ? 1 : 0, code: menuSelectedKeys[0], dashboardCode: record.code });
- }
- }else {
- message.error('请先选择左侧用户组/用户');
- }
- }}/>
- </Content>
- </Layout>
- }
- }
- class MenuList extends React.Component {
- componentDidMount() {
- this.addEvents();
- }
- addEvents = () => {
- const { refName } = this.props;
- let tabs = this['menuList-' + refName];
- let menu = tabs.getElementsByClassName('ant-menu')[0];
- menu.removeEventListener('scroll', this.onMenuScroll);
- menu.addEventListener('scroll', this.onMenuScroll);
- }
- onMenuScroll = (e) => {
- const { onScrollToBottom } = this.props;
- let target = e.target;
- if(target.scrollHeight - target.offsetHeight === target.scrollTop) { // 滚动到底了
- typeof onScrollToBottom === 'function' && onScrollToBottom()
- }
- }
- render() {
- const { refName, selectedKeys, list, filterLabel: pFilterLabel, filterLabelChange, displayField, valueField, onItemClick, limit } = this.props;
- const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
- let filterLabel = (pFilterLabel || '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
- return <div ref={node => this['menuList-' + refName] = node} className='menu-list'>
- <div className='search-area'>
- <Search
- value={pFilterLabel}
- placeholder="请输入关键字"
- onChange={e => {
- let val = e.target.value + '';
- typeof filterLabelChange === 'function' && filterLabelChange(val);
- }}
- />
- </div>
- <Menu
- selectedKeys={selectedKeys}
- >
- {list.filter(l => {
- let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
- return ((l[displayField] || '').search(reg) !== -1 || l[valueField] === selectedKeys[0]);
- }).slice(0, limit).map(l => (
- <Menu.Item key={l[valueField]+''} onClick={() => {
- typeof onItemClick === 'function' && onItemClick(l);
- }} >
- <div className='item-title'>
- <span className='label' title={l[displayField]}>
- { filterLabel ?
- ((l[displayField] || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
- return (
- fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
- <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
- fragment
- )
- }
- )) : <span>{l[displayField]}</span>
- }
- </span>
- </div>
- </Menu.Item>
- ))}
- </Menu>
- </div>
- }
- }
- class DashBoardTree extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- tableScrollHeight: 0,
- allExpanded: false,
- expandedRowKeys: [],
- }
- }
- componentDidMount() {
- this.tableSize();
- window.addEventListener('resize', this.tableSize);
- }
- componentWillUnmount() {
- window.removeEventListener('resize', this.tableSize);
- }
- tableSize = () => {
- const content = document.getElementsByClassName('content-setting')[0];
- const tableWrapper = content.getElementsByClassName('ant-table-wrapper')[0];
- const tableContent = tableWrapper.getElementsByClassName('ant-spin-nested-loading')[0];
- const tableHeader = tableContent.querySelector('thead');
- const padding = tableContent.getBoundingClientRect().top - tableWrapper.getBoundingClientRect().top;
- // 容器高度 - padding * 2 - 表头高度 - 边框线
- let tableScrollHeight = tableWrapper.offsetHeight - padding * 2 - tableHeader.offsetHeight - 2;
- this.setState({
- tableScrollHeight,
- });
- }
- onExpandedRowsChange = (expandedRowKeys) => {
- this.setState({
- expandedRowKeys
- });
- }
- onExpandAll = () => {
- const { dataSource } = this.props;
- let expandedRowKeys = this.getAllKeys(dataSource);
- this.setState({
- allExpanded: true,
- expandedRowKeys
- });
- }
- collapseAll = () => {
- this.setState({
- allExpanded: false,
- expandedRowKeys: []
- });
- }
- getAllKeys = (list) => {
- let keyArr = [];
- for(let i = 0;!!list && i < list.length; i++) {
- if(!!list[i].children) {
- keyArr.push(list[i].key);
- keyArr = keyArr.concat(this.getAllKeys(list[i].children, keyArr));
- }
- }
- return keyArr
- }
- render() {
- const { dataSource, onRowCheckChange, filterLabel } = this.props;
- const { tableScrollHeight, expandedRowKeys, allExpanded } = this.state;
- const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
- const columns = [{
- title: <span><Icon type='menu' title='展开/折叠全部' style={{ marginRight: '4px' }} onClick={allExpanded ? this.collapseAll : this.onExpandAll}/>报表目录</span>,
- dataIndex: 'name',
- key: 'name',
- width: '80%',
- render: (text, record) => {
- return <span style={{ fontWeight: record.type === 'dashboard' ? 'bold' : 'normal' }}>
- {
- filterLabel ?
- ((text || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
- return (
- fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
- <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
- fragment
- )
- }
- )) : text
- }
- </span>
- }
- }, {
- title: '查看',
- dataIndex: 'view',
- key: 'view',
- width: '20%',
- render: (text, record, index) => {
- return <Checkbox disabled={record.type === 'menu' && (!record.children || record.children.filter(c => c.type === 'dashboard').length === 0)} indeterminate={record.indeterminate} checked={record.checked} onChange={(e) => {
- let checked = e.target.checked;
- typeof onRowCheckChange === 'function' && onRowCheckChange(record, checked);
- }} />
- }
- }];
- return <Table columns={columns} dataSource={dataSource} scroll={{x: false, y: tableScrollHeight}}
- size='small'
- pagination={false}
- expandedRowKeys={expandedRowKeys}
- onExpandedRowsChange={this.onExpandedRowsChange}
- />
- }
- }
- export default connect(({ present: { authority, userGroup, user, dashboard } }) => ({ authority, userGroup, user, dashboard }))(Authority)
|