| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- /**
- * 图表列表
- */
- import React from 'react'
- import { Layout, Button, Icon, Input, Menu, Dropdown, Card, Col, Row, Popover, Breadcrumb, Tree, Tag } from 'antd'
- import { connect } from 'dva'
- import './list.less'
- import ChooseDataSourceBox from './chooseDataSourceBox'
- import { dateFormat } from '../../utils/baseUtils'
- import Ellipsis from 'ant-design-pro/lib/Ellipsis'
- import 'ant-design-pro/dist/ant-design-pro.css'
- import GroupSelector from '../datasource/groupSelector'
- import Thumbnail from './thumbnail'
- import DistributeBox from './distributeBox';
- import TransferBox from './transferBox'
- import DeleteBox from '../common/deleteBox'
- const { Content } = Layout
- const { Search } = Input
- const CardGrid = Card.Grid
- const { TreeNode } = Tree
- class ChartList extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- selectedRecord: null,
- visibleChooseDataSourceBox: false,
- visibleDistributeBox: false,
- visibleTransferBox: false,
- visibleGroupMenu: false, // 显示分组菜单
- visibleDeleteBox: false
- }
- }
- componentDidMount() {
- const { dispatch } = this.props;
- this.setBodyWidth();
- dispatch({ type: 'chart/fetchList' });
- dispatch({ type: 'chart/remoteGroupList' });
- }
- /**
- * 设置卡片容器宽度 = 每行最大卡片数量 * 卡片宽度
- */
- setBodyWidth() {
- const chartBody = document.getElementsByClassName('chart-body')[0]; // 卡片容器
- const parent = chartBody.parentNode; // 父级容器
- const pWidth = parent.offsetWidth; // 父级容器宽度
- const pPadding = 10 + 10; // 父级容器左右padding
- const cWidth = 207; // 每个卡片宽度
- const cMargin = 5 + 5; // 每个卡片左右margin
- const pTrueWidth = pWidth - pPadding; // 父容器实际可用宽度
- const cTrueWidth = cWidth + cMargin; // 卡片实际占用宽度
- const count = Math.floor(pTrueWidth/cTrueWidth); // 每行最大卡片数量
- chartBody.style.width = count * cTrueWidth + 'px';
- }
- generateCard() {
- const { chart, dispatch } = this.props;
- const groupList = chart.groupList;
- const { selectedRecord } = this.state;
- const list = chart.list;
- const currentGroup = chart.currentGroup;
- const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
- let filterLabel = chart.filterLabel.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
- const operationMenu = (
- <Menu className='menu-operation'>
- <Menu.Item onClick={() => {
- const { selectedRecord } = this.state;
- // const selectedChartDataSourceCode = selectedRecord ? selectedRecord.code : '';
- const selectedChartCode = selectedRecord ? selectedRecord.code : '';
- dispatch({ type: 'chartPolicy/fetchList', chartCode: selectedChartCode });
- // dispatch({ type: 'chartDesigner/remoteDataColumn', code: });
- this.setState({visibleDistributeBox: true})
- }}>
- <Icon type='share-alt'/>分发
- </Menu.Item>
- <Menu.SubMenu className='setgroupmenu' title={<div><Icon style={{ marginRight: '6px' }} type='profile' />移动到</div>}>
- {this.createGroupMenu(selectedRecord)}
- </Menu.SubMenu>
- <Menu.Divider />
- <Menu.Item
- onClick={()=>{
- this.setState({ visibleTransferBox: true})
- }}
- >
- <Icon type="swap" />移交
- </Menu.Item>
- <Menu.Item
- onClick={(e) => {
- this.setState({ visibleDeleteBox: true})
- }}
- >
- <Icon type="delete" />删除
-
- </Menu.Item>
- </Menu>
- )
-
- let groupFilter = groupList.concat({ code: '-1', label: '未分组' }).filter(g => (
- currentGroup[0].code === 'all' ||
- (
- currentGroup[0].code === '-1' ? g.code === '-1' : (
- currentGroup[1] ? (g.code === currentGroup[1].code) :
- (
- g.code === currentGroup[0].code ||
- g.pcode === currentGroup[0].code
- )
- )
- )
- )).map(g => g.code);
- let cards = list.filter(l => groupFilter.indexOf(l.groupCode+'') !== -1).map(l => {
- let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
- if((l.name || '').search(reg) !== -1 || (l.description || '').search(reg) !== -1) {
- return { ...l, bf: false };
- }else {
- return { ...l, bf: true };
- }
- }).sort((a, b) => {
- return new Date(b.createTime) - new Date(a.createTime)
- }).map( (l, i) => (
- <CardGrid className={`chart-card${l.bf?' chart-card-hide':''}`} 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: 'chartDesigner/reset' });
- dispatch({ type: 'main/redirect', path: '/chart/' + l.code });
- }}>
- {/* <div className='deny-body'>
- <div className='deny-tip'>您没有对应数据源的权限</div>
- </div> */}
- <Thumbnail type={l.type} code={l.code} option={l.chartOption}/>
- </Row>
- <Row className='desc'>
- <Ellipsis tooltip={l.description&&l.description.length > 16} lines={2}>{
- <span>
- { filterLabel ?
- ((l.description || '').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.description
- }
- </span>
- }</Ellipsis>
- </Row>
- <Row className='footer' type='flex' justify='end' align='bottom'>
- <Col style={{ textAlign: 'left' }} span={22}>
- <Row>{l.creator} {dateFormat(l.createTime, '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) {
- cards = <div style={{ padding: '7px', textAlign: 'center', fontSize: '14px', color: 'rgba(0, 0, 0, 0.45)' }}>暂无数据</div>
- }
- return cards;
- }
- createGroupMenu = (selectedRecord) => {
- const { chart, dispatch } = this.props;
- const groupList = chart.groupList;
- const pGroups = groupList.filter(d => d.pcode === '-1').sort((a, b) => a.index - b.index);
- const cGroups = groupList.filter(d => d.pcode !== '-1');
- let allGroups = !!selectedRecord ? [
- { code: '-1', label: '未分组' }
- ].concat(pGroups) : [
- { code: 'all', label: '全部分组' },
- { code: '-1', label: '未分组' }
- ].concat(pGroups);
- return allGroups.map(p => {
- let c = cGroups.filter(c => c.pcode === p.code).sort((a, b) => a.index - b.index);
- return c.length > 0 ? (
- <Menu.SubMenu key={p.code} title={<span style={{ fontWeight: !!selectedRecord ?
- (p.code+'' === selectedRecord.groupCode+'' ? 'bold' : (
- c.find(ch => ch.code+'' === selectedRecord.groupCode+'') && c.find(ch => ch.code+'' === selectedRecord.groupCode+'').pcode === p.code ? 'bold' : 'normal'
- ))
- : chart.currentGroup[0].code === p.code ? 'bold' : 'normal' }}>{p.label}</span>} onTitleClick={(item) => {
- dispatch({ type: 'chart/setCurrentGroup', group1: p });
- if(selectedRecord) {
- dispatch({ type: 'chart/remoteSetChartGroup', chart: selectedRecord, group: p });
- }
- this.hideGroupMenu();
- }}>
- {c.map(c => {
- return (<Menu.Item key={c.code} onClick={(item) => {
- dispatch({ type: 'chart/setCurrentGroup', group1: p, group2: c });
- if(selectedRecord) {
- dispatch({ type: 'chart/remoteSetChartGroup', chart: selectedRecord, group: c });
- }
- }}><span style={{ fontWeight: !!selectedRecord ? (
- selectedRecord.groupCode+'' === c.code+'' ? 'bold' : 'normal'
- ) : (chart.currentGroup[1] && (chart.currentGroup[1].code === c.code) ? 'bold' : 'normal') }}>{c.label}</span></Menu.Item>)
- })}
- </Menu.SubMenu>
- ) : (
- <Menu.Item key={p.code} onClick={() => {
- dispatch({ type: 'chart/setCurrentGroup', group1: p });
- if(selectedRecord) {
- dispatch({ type: 'chart/remoteSetChartGroup', chart: selectedRecord, group: p });
- }
- this.hideGroupMenu();
- }}><span style={{ fontWeight: !!selectedRecord ? (
- selectedRecord.groupCode+'' === p.code+'' ? 'bold' : 'normal'
- ) : chart.currentGroup[0] && (chart.currentGroup[0].code === p.code) ? 'bold' : 'normal' }}>{p.label}</span></Menu.Item>
- );
- });
- }
- createSubGroupMenu = () => {
- const { chart, dispatch } = this.props;
- const groupList = chart.groupList;
- const parentGroup = chart.currentGroup[0];
- const children = groupList.filter(d => d.pcode === parentGroup.code);
- const subGroup = chart.currentGroup[1];
- return children.map(c => {
- return (
- <Menu.Item key={c.code} onClick={() => {
- dispatch({ type: 'chart/setCurrentGroup', group1: parentGroup, group2: c });
- }}><span style={{ fontWeight: subGroup && (subGroup.code === c.code) ? 'bold' : 'normal' }}>{c.label}</span></Menu.Item>
- );
- })
- }
- createGroupTree(modify) {
- const { dispatch, chart } = this.props;
- const { groupEditing } = this.state;
- const groupList = chart.groupList;
- let parent = groupList.filter(d => d.pcode === '-1').sort((a, b) => a.index - b.index);
- let children = groupList.filter(d => d.pcode !== '-1');
- let groupTree = parent.map(p => {
- return (
- <TreeNode disabled={groupEditing} title={
- modify ? (<div><Icon style={{ cursor: 'move' }} type='drag'/>
- <Input value={p.label} size='small' focus={'true'} onFocus={() => {
- this.setState({
- groupEditing: true
- });
- }} onChange={(e) => {
- dispatch({ type: 'chart/modifyGroup', group: {...p, label:e.target.value} });
- }} onBlur={(e) => {
- this.setState({
- groupEditing: false
- });
- dispatch({ type: 'chart/remoteModifyGroup', group: {...p, label:e.target.value} });
- }} onPressEnter={(e) => {
- dispatch({ type: 'chart/remoteModifyGroup', group: {...p, label:e.target.value} });
- }} /><Icon type='plus-circle-o' onClick={() => {
- dispatch({ type: 'chart/remoteAddGroup', pgroup: p });
- }}/><Icon type='minus-circle' onClick={() => {
- dispatch({ type: 'chart/remoteDeleteGroup', group: p });
- }}/></div>) : p.label} key={p.code}>
- {
- children.filter(c => c.pcode === p.code).sort((a, b) => a.index - b.index).map(c => {
- return (
- <TreeNode disabled={groupEditing} title={
- modify ? (<div><Icon style={{ cursor: 'move' }} type='drag'/>
- <Input value={c.label} size='small' onFocus={() => {
- this.setState({
- groupEditing: true
- });
- }} onChange={(e) => {
- dispatch({ type: 'chart/modifyGroup', group: {...c, label:e.target.value} });
- }} onBlur={(e) => {
- this.setState({
- groupEditing: false
- });
- dispatch({ type: 'chart/remoteModifyGroup', group: {...c, label:e.target.value} });
- }} onPressEnter={(e) => {
- dispatch({ type: 'chart/remoteModifyGroup', group: {...c, label:e.target.value} });
- }} onCompositionEnd={(e) => {
- console.log(e.target.value);
- }}/><Icon type='minus-circle' onClick={() => {
- dispatch({ type: 'chart/remoteDeleteGroup', group: c });
- }}/></div>) : p.label
- } key={c.code} />
- )
- })
- }
- </TreeNode>
- )
- });
- return groupTree;
- }
- handleVisibleChange = (flag) => {
- this.setState({ visibleGroupMenu: flag });
- }
- hideGroupMenu = () => {
- this.setState({
- visibleGrouMenu: false
- });
- }
- onDrop = (info) => {
- const { dispatch } = this.props;
- const dropCode = info.node.props.eventKey;
- const dragCode = info.dragNode.props.eventKey;
- const dropPos = info.node.props.pos.split('-');
- const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]); // -1/0/1 -> 兄/子/弟
- console.log(dragCode, dropCode, dropPosition);
- dispatch({ type: 'chart/remoteMoveGroup', dragCode, dropCode, dropPosition });
- }
- render() {
- const { visibleChooseDataSourceBox, visibleDistributeBox, visibleTransferBox, visibleDeleteBox, selectedRecord } = this.state;
- const { dispatch, chart } = this.props;
- const TAG_COLOR = ['blue'];
- return (
- <Layout className='chart-list'>
- <Content>
- <Card title={
- <Row className='tools' type='flex' justify='space-between'>
- <Col style={{ display: 'flex' }}>
- <Popover overlayClassName='popover-group' title={
- <Row className='grouptree-title' type='flex' justify='space-between'>
- <Col>
- 分组管理
- </Col>
- <Col>
- <div className='create-group' onClick={() => {
- dispatch({ type: 'chart/remoteAddGroup' });
- }}>添加分组<Icon type="plus-circle-o" /></div>
- </Col>
- </Row>
- } trigger="click" placement="bottomLeft" content={(
- <Tree
- className='tree-group'
- showLine
- defaultExpandAll
- draggable
- onDragStart={this.onDragStart}
- onDrop={this.onDrop}
- >
- {
- this.createGroupTree(true)
- }
- </Tree>
- )}>
- <Icon type="bars" />
- </Popover>
- <Breadcrumb className='group' separator=">">
- <Breadcrumb.Item>
- <GroupSelector model={chart} modelName='chart'>
- <Tag color={TAG_COLOR[Math.ceil(Math.random()*TAG_COLOR.length) - 1]} >
- {chart.currentGroup[0].label}
- </Tag>
- </GroupSelector>
- </Breadcrumb.Item>
- {chart.currentGroup[1] && (<Breadcrumb.Item>
- <GroupSelector model={chart} modelName='chart'>
- <Tag color={TAG_COLOR[Math.ceil(Math.random()*TAG_COLOR.length) - 1]}>
- {chart.currentGroup[1].label}
- </Tag>
- </GroupSelector>
- </Breadcrumb.Item>)}
- </Breadcrumb>
- </Col>
- <Col className='search'>
- <Col style={{ padding: '0 5px' }}>
- <Search
- placeholder="请输入关键字"
- value={chart.filterLabel}
- onChange={e => {
- dispatch({ type: 'chart/setFilterLabel', label: e.target.value });
- }}
- />
- </Col>
- <Col >
- <Button onClick={() => {
- dispatch({ type: 'dataSource/fetchList' });
- this.setState({
- visibleChooseDataSourceBox: true
- });
- }}>
- <Icon type="area-chart" />创建图表
- </Button>
- <ChooseDataSourceBox visibleChooseDataSourceBox={visibleChooseDataSourceBox} hideBox={() => {
- this.setState({
- visibleChooseDataSourceBox: false
- });
- }}/>
- </Col>
- </Col>
- </Row>
- }>
- <div className='chart-body'>
- { this.generateCard() }
- </div>
- </Card>
- </Content>
- <DistributeBox key={this.state.selectedRecord ? this.state.selectedRecord.code : 'notkey'} visibleDistributeBox={visibleDistributeBox} selectedRecord={this.state.selectedRecord} hideBox={() => {
- this.setState({
- visibleDistributeBox: false
- });
- }} />
- <TransferBox
- visibleTransferBox={visibleTransferBox}
- onOk={(obj) => {
- console.log(obj);
- }}
- hideBox={() => {
- this.setState({
- visibleTransferBox: false
- })
- }} />
- <DeleteBox
- visibleDeleteBox={visibleDeleteBox}
- type='chart'
- hideBox={() => {
- this.setState({
- visibleDeleteBox: false
- })
- }}
- selectedRecord={selectedRecord}
- onOk={() => {
- dispatch({ type: 'chart/remoteDelete', code: this.state.selectedRecord.code })
- this.setState({ visibleDeleteBox: false })
- }} />
- </Layout>
- )
- }
- }
- function mapStateToProps(state) {
- return { chart: state.present.chart }
- }
- export default connect(mapStateToProps)(ChartList)
|