|
|
@@ -1,15 +1,14 @@
|
|
|
import React from 'react'
|
|
|
-import { Layout, Button, Icon, Input, Menu, Dropdown, Card, Col, Row } from 'antd'
|
|
|
+import { Layout, Button, Icon, Input, Table, Menu, Dropdown, Card, Col, Row } from 'antd'
|
|
|
import { connect } from 'dva'
|
|
|
-import moment from 'moment'
|
|
|
import TransferBox from '../common/selectUserBox/selectUserBox';
|
|
|
import AccessObjectBox from '../common/accessObjectBox/accessObjectBox'
|
|
|
-import Thumbnail from './thumbnail'
|
|
|
-import './list.less'
|
|
|
+import { dateFormat } from '../../utils/baseUtils'
|
|
|
import DeleteBox from '../common/deleteBox/deleteBox'
|
|
|
+import ShareBox from './shareBox'
|
|
|
+import './list.less'
|
|
|
const { Content } = Layout
|
|
|
const { Search } = Input
|
|
|
-const CardGrid = Card.Grid
|
|
|
|
|
|
|
|
|
class DashboardList extends React.Component {
|
|
|
@@ -19,6 +18,8 @@ class DashboardList extends React.Component {
|
|
|
selectedRecord: null,
|
|
|
visibleChooseDataSourceBox: false,
|
|
|
visibleDistributeBox: false,
|
|
|
+ visibleShareBox: false,
|
|
|
+ shareUrl: '',
|
|
|
visibleTransferBox: false,
|
|
|
visibleGroupMenu: false, // 显示分组菜单
|
|
|
visibleDeleteBox: false,
|
|
|
@@ -29,31 +30,7 @@ class DashboardList extends React.Component {
|
|
|
|
|
|
componentDidMount() {
|
|
|
const { dispatch } = this.props;
|
|
|
- this.setBodyWidth();
|
|
|
dispatch({ type: 'dashboard/fetchList' });
|
|
|
- window.addEventListener('resize', this.setBodyWidth);
|
|
|
- }
|
|
|
-
|
|
|
- componentWillUnmount() {
|
|
|
- window.removeEventListener('resize', this.setBodyWidth)
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 设置卡片容器宽度 = 每行最大卡片数量 * 卡片宽度
|
|
|
- */
|
|
|
- setBodyWidth() {
|
|
|
- const cardBody = document.getElementsByClassName('dashboard-body')[0]; // 卡片容器
|
|
|
- const parent = cardBody.parentNode; // 父级容器
|
|
|
- const pWidth = parent.offsetWidth; // 父级容器宽度
|
|
|
- const pPadding = 10 + 10; // 父级容器左右padding
|
|
|
- const cWidth = 512; // 每个卡片宽度
|
|
|
- const cMargin = 8 + 8; // 每个卡片左右margin
|
|
|
- const pTrueWidth = pWidth - pPadding; // 父容器实际可用宽度
|
|
|
- const cTrueWidth = cWidth + cMargin; // 卡片实际占用宽度
|
|
|
- const count = Math.floor(pTrueWidth/cTrueWidth); // 每行最大卡片数量
|
|
|
- const cardBodyWidth = count * cTrueWidth;
|
|
|
-
|
|
|
- cardBodyWidth > 0 ? cardBody.style.width = cardBodyWidth + 'px' : void(0);
|
|
|
}
|
|
|
|
|
|
getShareList = () => {
|
|
|
@@ -83,24 +60,70 @@ class DashboardList extends React.Component {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- generateCard() {
|
|
|
- const { main, dashboard, dispatch } = this.props;
|
|
|
+ handleVisibleChange = (flag) => {
|
|
|
+ this.setState({ visibleGroupMenu: flag });
|
|
|
+ }
|
|
|
+
|
|
|
+ hideGroupMenu = () => {
|
|
|
+ this.setState({
|
|
|
+ visibleGrouMenu: false
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ distribute = (group, geren) => {
|
|
|
+ const { dispatch } = this.props;
|
|
|
const { selectedRecord } = this.state;
|
|
|
+ let targets = group.map(g => ({
|
|
|
+ code: g.code,
|
|
|
+ name: g.name,
|
|
|
+ isGroup: true
|
|
|
+ })).concat(geren.map(g => ({
|
|
|
+ code: g.code,
|
|
|
+ name: g.name,
|
|
|
+ isGroup: false
|
|
|
+ })))
|
|
|
+ dispatch({ type: 'dashboard/share', code: selectedRecord.code, targets });
|
|
|
+ }
|
|
|
+
|
|
|
+ onSearch(list, text) {
|
|
|
+ const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
|
|
|
+ let filterLabel = (text || '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
|
|
|
+ return list.map(l => {
|
|
|
+ let o = Object.assign({}, l);
|
|
|
+ let reg = new RegExp('('+ filterLabel +'){1}', 'ig');
|
|
|
+ if(o.name && o.name.search(reg) !== -1) {
|
|
|
+ return o;
|
|
|
+ }else if(o.description && o.description.search(reg) !== -1) {
|
|
|
+ return o;
|
|
|
+ }else {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ }).filter(a => a!==null);
|
|
|
+ }
|
|
|
+
|
|
|
+ onSort(list) {
|
|
|
+ return list.sort((a, b) => {
|
|
|
+ return new Date(b.createTime) - new Date(a.createTime);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ render() {
|
|
|
+ const { dispatch, dashboard, main } = this.props;
|
|
|
+ const { visibleShareBox, shareUrl, visibleDistributeBox, visibleTransferBox, visibleDeleteBox, selectedRecord, defaultSelectedGroups, defaultSelectedUsers } = this.state
|
|
|
const { currentUser } = main;
|
|
|
- const list = dashboard.list;
|
|
|
|
|
|
const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
|
|
|
let filterLabel = dashboard.filterLabel.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
|
|
|
|
|
|
- const operationMenu = (
|
|
|
+ const moreOperatingMenu = (
|
|
|
<Menu className='menu-operation'>
|
|
|
- <Menu.Item
|
|
|
- disabled
|
|
|
- onClick={(e) => {
|
|
|
- }}
|
|
|
- >
|
|
|
- <Icon type="delete" />分享
|
|
|
- </Menu.Item>
|
|
|
+ { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item onClick={() => {
|
|
|
+ dispatch({ type: 'dashboard/getShareKey', record: selectedRecord, delay: 7 }).then((key) => {
|
|
|
+ this.setState({ visibleShareBox: true, shareUrl: window.location.origin + '/#/dashboard/share_key/' + key })
|
|
|
+ });
|
|
|
+ }}>
|
|
|
+ <Icon type='share-alt'/>分享
|
|
|
+ </Menu.Item>}
|
|
|
{ selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Divider />}
|
|
|
{ selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item onClick={this.getShareList}>
|
|
|
<Icon type='share-alt'/>分发
|
|
|
@@ -122,99 +145,79 @@ class DashboardList extends React.Component {
|
|
|
</Menu>
|
|
|
)
|
|
|
|
|
|
- let cards = list.filter(l => {
|
|
|
- let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
|
|
|
- return (l.name || '').search(reg) !== -1;
|
|
|
- }).sort((a, b) => {
|
|
|
- return new Date(b.createTime) - new Date(a.createTime)
|
|
|
- }).map( (l, i) => (
|
|
|
- <CardGrid className='dashboard-card' key={i} onClick={() => {
|
|
|
- this.setState({ selectedRecord: l })
|
|
|
- }}>
|
|
|
- <Card
|
|
|
- title={
|
|
|
- <Row type='flex' justify='space-between'>
|
|
|
- <Col span={21} style={{ overflow: 'hidden', textOverflow: 'ellipsis' }} >
|
|
|
- { filterLabel ?
|
|
|
- ((l.name || '').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
|
|
|
- )
|
|
|
- }
|
|
|
- )) : l.name
|
|
|
- }
|
|
|
- </Col>
|
|
|
- <Col style={{ textAlign: 'right' }} span={3} >
|
|
|
- {/* <Icon type='star-o'/> */}
|
|
|
- </Col>
|
|
|
- </Row>
|
|
|
- }
|
|
|
- cover={
|
|
|
- <Col className='cover-body'>
|
|
|
- <Row className='thumb' onClick={() => {
|
|
|
- dispatch({ type: 'dashboardDesigner/reset' });
|
|
|
- dispatch({ type: 'main/redirect', path: '/dashboard/' + l.code });
|
|
|
- dispatch({ type: 'recent/addRecentRecord', tarId: l.code, recordType: 1});
|
|
|
- }}>
|
|
|
- <Thumbnail type={l.type} code={l.code} option={l.chartOption} thumbnail={l.thumbnail}/>
|
|
|
- </Row>
|
|
|
- <Row className='footer' type='flex' justify='end' align='bottom'>
|
|
|
- <Col style={{ textAlign: 'left' }} span={22}>
|
|
|
- <Row>{l.creatorName} {moment(l.createTime).format('YYYY-MM-DD')}</Row>
|
|
|
- </Col>
|
|
|
- <Col span={2} style={{ textAlign: 'right' }}>
|
|
|
- <Dropdown overlay={operationMenu} trigger={['click']}>
|
|
|
- <Icon type="ellipsis" />
|
|
|
- </Dropdown>
|
|
|
- </Col>
|
|
|
- </Row>
|
|
|
- </Col>
|
|
|
- }
|
|
|
- >
|
|
|
- </Card>
|
|
|
- </CardGrid>
|
|
|
- ));
|
|
|
- if(cards.length === 0) {
|
|
|
- return (<div className="ant-empty ant-empty-normal"><div className="ant-empty-image"><img alt="暂无数据" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAxKSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgIDxlbGxpcHNlIGZpbGw9IiNGNUY1RjUiIGN4PSIzMiIgY3k9IjMzIiByeD0iMzIiIHJ5PSI3Ii8+CiAgICA8ZyBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0iI0Q5RDlEOSI+CiAgICAgIDxwYXRoIGQ9Ik01NSAxMi43Nkw0NC44NTQgMS4yNThDNDQuMzY3LjQ3NCA0My42NTYgMCA0Mi45MDcgMEgyMS4wOTNjLS43NDkgMC0xLjQ2LjQ3NC0xLjk0NyAxLjI1N0w5IDEyLjc2MVYyMmg0NnYtOS4yNHoiLz4KICAgICAgPHBhdGggZD0iTTQxLjYxMyAxNS45MzFjMC0xLjYwNS45OTQtMi45MyAyLjIyNy0yLjkzMUg1NXYxOC4xMzdDNTUgMzMuMjYgNTMuNjggMzUgNTIuMDUgMzVoLTQwLjFDMTAuMzIgMzUgOSAzMy4yNTkgOSAzMS4xMzdWMTNoMTEuMTZjMS4yMzMgMCAyLjIyNyAxLjMyMyAyLjIyNyAyLjkyOHYuMDIyYzAgMS42MDUgMS4wMDUgMi45MDEgMi4yMzcgMi45MDFoMTQuNzUyYzEuMjMyIDAgMi4yMzctMS4zMDggMi4yMzctMi45MTN2LS4wMDd6IiBmaWxsPSIjRkFGQUZBIi8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K"/></div><p className="ant-empty-description">暂无数据</p></div>)
|
|
|
- }
|
|
|
- return cards;
|
|
|
- }
|
|
|
-
|
|
|
- handleVisibleChange = (flag) => {
|
|
|
- this.setState({ visibleGroupMenu: flag });
|
|
|
- }
|
|
|
-
|
|
|
- hideGroupMenu = () => {
|
|
|
- this.setState({
|
|
|
- visibleGrouMenu: false
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- distribute = (group, geren) => {
|
|
|
- const { dispatch } = this.props;
|
|
|
- const { selectedRecord } = this.state;
|
|
|
- let targets = group.map(g => ({
|
|
|
- code: g.code,
|
|
|
- name: g.name,
|
|
|
- isGroup: true
|
|
|
- })).concat(geren.map(g => ({
|
|
|
- code: g.code,
|
|
|
- name: g.name,
|
|
|
- isGroup: false
|
|
|
- })))
|
|
|
- dispatch({ type: 'dashboard/share', code: selectedRecord.code, targets });
|
|
|
- }
|
|
|
+ const dashboardColumns = [{
|
|
|
+ title: '名称',
|
|
|
+ dataIndex: 'name',
|
|
|
+ key: 'name',
|
|
|
+ width: 100,
|
|
|
+ render: (text, record) => {
|
|
|
+ return (
|
|
|
+ <span style={{ color: '#1890ff', cursor: 'pointer' }} onClick={() => {
|
|
|
+ dispatch({ type: 'dashboardDesigner/reset' });
|
|
|
+ dispatch({ type: 'main/redirect', path: '/dashboard/' + record.code });
|
|
|
+ dispatch({ type: 'recent/addRecentRecord', tarId: record.code, recordType: 1});
|
|
|
+ }}>
|
|
|
+ { 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: 'description',
|
|
|
+ key: 'description',
|
|
|
+ width: 200,
|
|
|
+ render: (text, record) => {
|
|
|
+ return (
|
|
|
+ <span>
|
|
|
+ { 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: 'creatorName',
|
|
|
+ key: 'creatorName',
|
|
|
+ width: 100
|
|
|
+ }, {
|
|
|
+ title: '创建时间',
|
|
|
+ dataIndex: 'createTime',
|
|
|
+ key: 'createTime',
|
|
|
+ render: (text, record) => dateFormat(text, 'yyyy-MM-dd hh:mm:ss'),
|
|
|
+ width: 100
|
|
|
+ }, {
|
|
|
+ title: '操作',
|
|
|
+ key: 'action',
|
|
|
+ render: (text, record, index) => (
|
|
|
+ <Dropdown code={record.code} overlay={moreOperatingMenu} trigger={['click']} >
|
|
|
+ <Icon type="setting" />
|
|
|
+ </Dropdown>
|
|
|
+ ),
|
|
|
+ width: 50
|
|
|
+ }];
|
|
|
|
|
|
- render() {
|
|
|
- const { dispatch, dashboard } = this.props;
|
|
|
- const { visibleDistributeBox, visibleTransferBox, visibleDeleteBox, selectedRecord, defaultSelectedGroups, defaultSelectedUsers } = this.state
|
|
|
return (
|
|
|
- <Layout className='dashboard-list'>
|
|
|
+ <Layout className='dashboard-view'>
|
|
|
<Content>
|
|
|
- <Card title={
|
|
|
- <Row className='tools' type='flex' justify='space-between'>
|
|
|
+ <Card className="dashboard-body" title={
|
|
|
+ <Row className='dashboard-tools' type='flex' justify='space-between'>
|
|
|
<Col style={{ display: 'flex' }}>
|
|
|
</Col>
|
|
|
<Col className='search'>
|
|
|
@@ -237,9 +240,28 @@ class DashboardList extends React.Component {
|
|
|
</Col>
|
|
|
</Row>
|
|
|
}>
|
|
|
- <div className='dashboard-body'>
|
|
|
+ {/* <div className='dashboard-body'>
|
|
|
{ this.generateCard() }
|
|
|
- </div>
|
|
|
+ </div> */}
|
|
|
+ <Table
|
|
|
+ className='dashboard-table'
|
|
|
+ columns={dashboardColumns}
|
|
|
+ dataSource={
|
|
|
+ this.onSort(
|
|
|
+ this.onSearch(dashboard.list, dashboard.filterLabel)
|
|
|
+ )
|
|
|
+ }
|
|
|
+ size='small'
|
|
|
+ scroll={{x: false, y: true}}
|
|
|
+ pagination={false}
|
|
|
+ onRow={(record) => {
|
|
|
+ return {
|
|
|
+ onClick: () => {
|
|
|
+ this.setState({ selectedRecord: record})
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ />
|
|
|
</Card>
|
|
|
</Content>
|
|
|
{visibleDistributeBox && <AccessObjectBox
|
|
|
@@ -275,7 +297,26 @@ class DashboardList extends React.Component {
|
|
|
}}
|
|
|
okHandler={() => {
|
|
|
dispatch({ type: 'dashboard/remoteDelete', code: this.state.selectedRecord.code })
|
|
|
- }} />}
|
|
|
+ }}
|
|
|
+ />}
|
|
|
+ {visibleShareBox && <ShareBox
|
|
|
+ visibleBox={visibleShareBox}
|
|
|
+ shareUrl={shareUrl}
|
|
|
+ hideBox={() => {
|
|
|
+ this.setState({
|
|
|
+ visibleShareBox: false
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ onRefreshKey={(delay) => {
|
|
|
+ return dispatch({ type: 'dashboard/getShareKey', record: this.state.selectedRecord, delay: delay })
|
|
|
+ .then((key) => {
|
|
|
+ this.setState({
|
|
|
+ shareUrl: window.location.origin + '/#/dashboard/share_key/' + key
|
|
|
+ })
|
|
|
+
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ />}
|
|
|
</Layout>
|
|
|
)
|
|
|
}
|