ソースを参照

看板编辑界面细节调整/数据源列设置【允许分组】生效/

zhuth 7 年 前
コミット
683c6021fd

BIN
public/images/dashboard-default.png


+ 0 - 2
src/components/chart/thumbnail.jsx

@@ -1,11 +1,9 @@
 import React from 'react'
 import Echarts from 'echarts-for-react'
-import { Table, Image } from 'antd'
 import resolveChartOption from './resolveChartOption'
 
 const Thumbnail = ({ type, code, option }) => {
     const newOption = resolveChartOption(option || {}, true, true);
-    const { columns, data } = newOption;
     const viewType = ['bar', 'line', 'pie', 'scatter'].indexOf(type) !== -1 ? 'echarts' :
         (['aggregateTable', 'dataView'].indexOf(type) !== -1 ? 'table' : 'default');
         

+ 1 - 1
src/components/chartDesigner/sections/ScatterConfigForm.jsx

@@ -174,7 +174,7 @@ const ScatterConfigForm = ({ autoRefresh, chartDesigner, dispatch, formItemLayou
 					}}
 					value={chartDesigner.scatterConfig.groupBy}
 				>
-					{columns.filter(c => c.type === 'categorical').map((c, i)=>{
+					{columns.filter(c => c.groupable).map((c, i)=>{
 						return (<Option key={i} value={c.name}>{c.label}</Option>)
 					})}
 				</Select>

+ 1 - 1
src/components/chartDesigner/sections/aggregateTableConfigForm.jsx

@@ -73,7 +73,7 @@ class AggregateTableConfigForm extends React.Component {
 						}}
 						value={chartDesigner.aggregateTableConfig.groupBy}
 					>
-						{columns.filter(c => c.type === 'categorical').map((c, i)=>{
+						{columns.filter(c => c.groupable).map((c, i)=>{
 							return (<Option key={i} value={c.name}>{c.label}</Option>)
 						})}
 					</Select>

+ 1 - 1
src/components/chartDesigner/sections/barConfigForm.jsx

@@ -173,7 +173,7 @@ const BarConfigForm = ({ autoRefresh, chartDesigner, dispatch, formItemLayout })
 					}}
 					value={chartDesigner.barConfig.groupBy}
 				>
-					{columns.filter(c => c.type === 'categorical').map((c, i)=>{
+					{columns.filter(c => c.groupable).map((c, i)=>{
 						return (<Option key={i} value={c.name}>{c.label}</Option>)
 					})}
 				</Select>

+ 1 - 1
src/components/chartDesigner/sections/lineConfigForm.jsx

@@ -173,7 +173,7 @@ const LineConfigForm = ({ autoRefresh, chartDesigner, dispatch, formItemLayout }
 					}}
 					value={chartDesigner.lineConfig.groupBy}
 				>
-					{columns.filter(c => c.type === 'categorical').map((c, i)=>{
+					{columns.filter(c => c.groupable).map((c, i)=>{
 						return (<Option key={i} value={c.name}>{c.label}</Option>)
 					})}
 				</Select>

+ 19 - 23
src/components/dashboard/list.jsx

@@ -1,18 +1,17 @@
 import React from 'react'
-import { Layout, Button, Icon, Input, Menu, Dropdown, Card, Col, Row, Popover, Breadcrumb, Tree, Tag } from 'antd'
+import { Layout, Button, Icon, Input, Menu, Dropdown, Card, Col, Row } from 'antd'
 import { connect } from 'dva'
 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 './list.less'
 const { Content } = Layout
 const { Search } = Input
 const CardGrid = Card.Grid
-const { TreeNode } = Tree
 
 
-class ChartList extends React.Component {
+class DashboardList extends React.Component {
     constructor(props) {
         super(props);
         this.state = {
@@ -27,24 +26,23 @@ class ChartList extends React.Component {
         const { dispatch } = this.props;
         this.setBodyWidth();
         dispatch({ type: 'dashboard/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 = 600; // 每个卡片宽度
-        // const cMargin = 5 + 5; // 每个卡片左右margin
-        // const pTrueWidth = pWidth - pPadding; // 父容器实际可用宽度
-        // const cTrueWidth = cWidth + cMargin; // 卡片实际占用宽度
-        // const count = Math.floor(pTrueWidth/cTrueWidth); // 每行最大卡片数量
+        const chartBody = document.getElementsByClassName('dashboard-body')[0]; // 卡片容器
+        const parent = chartBody.parentNode; // 父级容器
+        const pWidth = parent.offsetWidth; // 父级容器宽度
+        const pPadding = 10 + 10; // 父级容器左右padding
+        const cWidth = 512; // 每个卡片宽度
+        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';
+        chartBody.style.width = count * cTrueWidth  + 'px';
     }
 
     generateCard() {
@@ -64,7 +62,7 @@ class ChartList extends React.Component {
                 </Menu.Item>
                 <Menu.Divider />
                 <Menu.Item onClick={() => {
-                    dispatch({ type: 'dashboard/remoteDelete', code: this.state.selectedRecord.code });
+                    dispatch({ type: 'dashboard/remoteDelete', code: selectedRecord.code });
                 }}>
                     <Icon type='delete'/>删除
                 </Menu.Item>
@@ -106,7 +104,7 @@ class ChartList extends React.Component {
                                 dispatch({ type: 'dashboardDesigner/reset' });
                                 dispatch({ type: 'main/redirect', path: '/dashboard/' + l.code });
                             }}>
-                                {/* <Thumbnail type={l.type} code={l.code} option={l.chartOption}/> */}
+                                <Thumbnail type={l.type} code={l.code} option={l.chartOption}/>
                             </Row>
                             <Row className='desc'>
                                 <Ellipsis tooltip={l.description.length > 16} lines={2}>{
@@ -157,9 +155,7 @@ class ChartList extends React.Component {
     }
 
     render() {
-        const { visibleChooseDataSourceBox, visibleDistributeBox } = this.state;
         const { dispatch, dashboard } = this.props;
-        const TAG_COLOR = ['blue'];
         return (
             <Layout className='dashboard-list'>
                 <Content>
@@ -187,7 +183,7 @@ class ChartList extends React.Component {
                             </Col>
                         </Row>
                     }>
-                        <div className='chart-body'>
+                        <div className='dashboard-body'>
                             { this.generateCard() }
                         </div>
                     </Card>
@@ -198,8 +194,8 @@ class ChartList extends React.Component {
 }
 
 
-function mapStateToProps({ present: { chart, dashboard } }) {
-    return { chart, dashboard }
+function mapStateToProps({ present: { dashboard } }) {
+    return { dashboard }
 }
 
-export default connect(mapStateToProps)(ChartList)
+export default connect(mapStateToProps)(DashboardList)

+ 2 - 2
src/components/dashboard/list.less

@@ -67,8 +67,8 @@
                             background-repeat: no-repeat;
                             cursor: pointer;
                         }
-                        .chart-default {
-                            background-image: url(/images/chart-default.png);
+                        .dashboard-default {
+                            background-image: url(/images/dashboard-default.png);
                             width: 100%;
                             height: 100%;
                             background-position: center;

+ 9 - 0
src/components/dashboard/thumbnail.jsx

@@ -0,0 +1,9 @@
+const Thumbnail = ({ type, code, option }) => {
+    return (
+        <div style={{ width: '100%', height: '100%' }}>
+            <div className='dashboard-default'></div>
+        </div>
+    );
+}
+
+export default Thumbnail;

+ 2 - 1
src/components/dashboardDesigner/chartView.jsx

@@ -3,12 +3,13 @@ import Echarts from 'echarts-for-react'
 import { connect } from 'dva'
 import { Table } from 'antd'
 import resolveChartOption from '../chart/resolveChartOption.js'
-import BraftEditor from 'braft-editor'
 import RichTextEditor from './richTextEditor'
 
 const ChartView = ({ item, chart, editMode, dispatch }) => {
     const { viewType, chartCode, content } = item;
     let children = <div className='chart-default mover'></div>;
+
+    console.log(editMode);
     if(viewType === 'chart') { // 图表类型
         let targetChart = chart.list.filter(c => c.code === chartCode)[0];
         if(targetChart) {

+ 2 - 4
src/components/dashboardDesigner/chooseChartBox.jsx

@@ -1,12 +1,10 @@
 import React from 'react'
 import '../../models/dashboardDesigner'
-import { Modal, Select, Checkbox, Row, Col, Table, Input, message } from 'antd'
+import { Modal, Checkbox, Row, Col, Table, Input, message } from 'antd'
 import { connect } from 'dva'
 import { dateFormat } from '../../utils/baseUtils'
-import BraftEditor from 'braft-editor'
 import 'braft-editor/dist/braft.css'
 import './chooseChartBox.less'
-const Option = Select.Option
 const { Search } = Input
 
 class ChooseChartBox extends React.Component {
@@ -65,7 +63,7 @@ class ChooseChartBox extends React.Component {
     }
 
     render() {
-        const { operation, visibleBox, hideBox, dashboardDesigner, chart, dispatch } = this.props;
+        const { visibleBox, hideBox, dashboardDesigner, chart } = this.props;
         const { selectedRecord } = this.state;
         const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
         let filterLabel = this.state.filterLabel.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号

+ 1 - 2
src/components/dashboardDesigner/header.jsx

@@ -1,5 +1,5 @@
 import React from 'react'
-import { Input, Icon, Button, Popconfirm, Dropdown, Menu } from 'antd'
+import { Input, Icon, Button, Popconfirm } from 'antd'
 import { connect } from 'dva'
 import './header.less'
 
@@ -17,7 +17,6 @@ class Header extends React.Component {
 
     render() {
         const { dashboardDesigner, dispatch } = this.props;
-        console.log(dashboardDesigner);
         return (
             <div className='dashboarddesigner-header'>
                 <div className='header-item toolbar-back'>

+ 1 - 2
src/components/dashboardDesigner/layout.jsx

@@ -1,8 +1,7 @@
 import React from 'react'
 import { connect } from 'dva'
-import { Icon, Menu, Layout, Dropdown, Spin } from 'antd'
+import { Icon, Layout, Spin } from 'antd'
 import './layout.less'
-import Element from './element'
 import html2canvas from 'html2canvas'
 import jsPDF from 'jspdf'
 import DashboardDesignerHeader from './header'

+ 8 - 0
src/components/dashboardDesigner/richTextEditor.jsx

@@ -12,6 +12,14 @@ class RichTextEditor extends Component {
         const elem = this.refs.editorElem;
         const editor = new E(elem);
         editor.customConfig.onchange = onContentChange; // 使用 onchange 函数监听内容的变化
+        editor.customConfig.onfocus = () => {
+            let toolbar = elem.getElementsByClassName('w-e-toolbar')[0];
+            toolbar.style.display = 'flex';
+        }; // 获得焦点时显示工具栏(需要计算位置)
+        editor.customConfig.onblur = function (html) {
+            let toolbar = elem.getElementsByClassName('w-e-toolbar')[0];
+            toolbar.style.display = 'none';
+        }
         editor.customConfig.uploadImgShowBase64 = true; // 上传图片(base64)
         editor.create()
         editor.txt.html(content);

+ 0 - 0
src/components/dashboardDesigner/viewBox.jsx


+ 50 - 40
src/components/dashboardDesigner/viewLayout.jsx

@@ -1,7 +1,7 @@
 import React from "react"
 import "./viewLayout.less"
 import ReactGridLayout from 'react-grid-layout'
-import { Icon } from 'antd'
+import { Icon, Modal } from 'antd'
 import { connect } from 'dva'
 import ChartView from './chartView'
 
@@ -9,46 +9,32 @@ class ViewLayout extends React.PureComponent {
     constructor(props) {
         super(props);
         this.state = {
-            items: [0, 1, 2, 3, 4].map(function (i, key, list) {
-                return {
-                    i: i.toString(),
-                    x: i * 2,
-                    y: 0,
-                    w: i + 1,
-                    h: 2,
-                    add: i === (list.length - 1).toString()
-                };
-            }),
-            newCounter: 0,
-            width: 1100,
-            editMode: true
+            visiblePreviewBox: false
         };
     }
 
-    createElement = (item) => {
-        const { dashboardDesigner, dispatch, editMode } = this.props;
-
-        return dashboardDesigner.items.map(item => {
-            const { code, title, viewType, layout, chartCode } = item;
-            return (
-                <div className={`chartview${editMode ? ' chartview-edit' : ''}`} key={code} data-grid={layout}>
-                    <div className='chartview-toolbar mover'>
-                        <div className='chart-title'><span>{title}</span></div>
-                        <div className='chart-tools'>
-                            <Icon type="arrows-alt" />
-                            {editMode && viewType!=='richText' &&  <Icon type='edit' onClick={() => {
-                                dispatch({ type: 'chartDesigner/reset' });
-                                dispatch({ type: 'main/redirect', path: '/chart/' + chartCode });
-                            }}/>}
-                            {editMode && <Icon type='delete' onClick={() => {
-                                dispatch({ type: 'dashboardDesigner/deleteItem', item });
-                            }} />}
-                        </div>
+    createElement = (item, isPreview) => {
+        const { dispatch, editMode } = this.props;
+        const { code, title, viewType, layout, chartCode } = item;
+        return (
+            <div className={`chartview${editMode ? ' chartview-edit' : ''}`} key={code} data-grid={layout}>
+                <div className='chartview-toolbar mover'>
+                    <div className='chart-title'><span>{title}</span></div>
+                    <div className='chart-tools'>
+                        {!isPreview && viewType !== 'richText' && <Icon type="arrows-alt" onClick={() => this.showPreviewBox(item)}/>}
+                        {editMode && viewType !== 'richText' &&  <Icon type='edit' onClick={() => {
+                            dispatch({ type: 'chartDesigner/reset' });
+                            dispatch({ type: 'main/redirect', path: '/chart/' + chartCode });
+                        }}/>}
+                        {!isPreview && editMode && <Icon type='delete' onClick={() => {
+                            dispatch({ type: 'dashboardDesigner/deleteItem', item });
+                        }} />}
+                        {isPreview && <Icon type="close" onClick={this.hidePreviewBox}/>}
                     </div>
-                    <ChartView editMode={editMode} item={{...item}}/>
                 </div>
-            )
-        });
+                <ChartView editMode={isPreview ? false : editMode} item={{...item}}/>
+            </div>
+        )
     }
 
     onLayoutChange = (layout) => {
@@ -59,10 +45,24 @@ class ViewLayout extends React.PureComponent {
         }, 200)
     }
 
+    showPreviewBox = (item) => {
+        this.setState({
+            previewItem: item,
+            visiblePreviewBox: true
+        });
+    }
+
+    hidePreviewBox = () => {
+        this.setState({
+            visiblePreviewBox: false
+        });
+    }
+
     render() {
-        const { width, editMode } = this.props;
-        const children = this.createElement();
-        return (
+        const { dashboardDesigner, width, editMode } = this.props;
+        const { visiblePreviewBox, previewItem } = this.state;
+        const children = dashboardDesigner.items.map((item) => this.createElement(item));
+        return (<div>
             <ReactGridLayout
                 width={width}
                 cols={12}
@@ -78,7 +78,17 @@ class ViewLayout extends React.PureComponent {
             >
                 {children}
             </ReactGridLayout>
-        );
+            <Modal
+                className='previewbox'
+                visible={visiblePreviewBox}
+                onCancel={this.hidePreviewBox}
+                footer={null}
+                keyboard={true}
+                maskClosable={true}
+                >
+                {previewItem && this.createElement(previewItem, true)}
+            </Modal>
+        </div>);
     }
 }
 

+ 95 - 73
src/components/dashboardDesigner/viewLayout.less

@@ -1,98 +1,120 @@
 
-  .react-grid-layout {
-    background: #eee;
-    .chartview {
-      padding-top: 30px;
-      .chartview-toolbar {
-        height: 30px;
-        margin-top: -30px;
-        display: flex;
-        padding: 0 10px;
-        justify-content: space-between;
-        .chart-title {
-          font-size: 20px;
-        }
-        .chart-tools {
-          display: none;
-          font-size: 20px;
-          .anticon {
-            margin-left: 10px;
-            cursor: pointer;
-          }
+  .chartview {
+    padding-top: 30px;
+    .chartview-toolbar {
+      height: 30px;
+      margin-top: -30px;
+      display: flex;
+      padding: 0 10px;
+      justify-content: space-between;
+      .chart-title {
+        font-size: 20px;
+      }
+      .chart-tools {
+        display: none;
+        font-size: 20px;
+        .anticon {
+          margin-left: 10px;
+          cursor: pointer;
         }
       }
-      .chartview-content {
-        .chart-default {
-          background-image: url(/images/chart-default.png);
-          width: 100%;
-          height: 100%;
-          background-position: center;
-          background-size: contain;
-          background-repeat: no-repeat;
+    }
+    .chartview-content {
+      .chart-default {
+        background-image: url(/images/chart-default.png);
+        width: 100%;
+        height: 100%;
+        background-position: center;
+        background-size: contain;
+        background-repeat: no-repeat;
+      }
+      .richtexteditor {
+        padding-bottom: 10px;
+        height: 100%;
+        .w-e-toolbar {
+          height: 30px;
+          background-color: white !important;
+          border: none !important;
+          position: fixed;
+          top: 0;
+          display: none;
         }
-        .richtexteditor {
-          padding-bottom: 10px;
-          height: 100%;
-          .w-e-toolbar {
-            height: 30px;
-            background-color: white !important;
-            border: none !important;
-            position: absolute;
-            top: 0;
-            display: none;
-          }
-          .w-e-text-container {
-            height: 100% !important;
-            pointer-events: none;
-            border: none !important;
-            .w-e-text {
-              overflow-y: auto;
-              padding: 0 14px;
-              &>table {
-                margin: 0;
-              }
-              &>p {
-                margin: 0;
-              }
+        .w-e-text-container {
+          height: 100% !important;
+          pointer-events: none;
+          border: none !important;
+          .w-e-text {
+            overflow-y: auto;
+            padding: 0 14px;
+            &>table {
+              margin: 0;
             }
-            .w-e-panel-container {
-              z-index:100000;
+            &>p {
+              margin: 0;
             }
           }
+          .w-e-panel-container {
+            z-index:100000;
+          }
         }
       }
-      &:hover {
-        .chart-tools {
-          display: block;
-          .anticon {
-            &:hover {
-              color: red;
-            }
+    }
+    &:hover {
+      .chart-tools {
+        display: block;
+        .anticon {
+          &:hover {
+            color: red;
           }
         }
       }
     }
-    .chartview-edit {
-      .chartview-toolbar {
+  }
+  .chartview-edit {
+    .chartview-toolbar {
+      cursor: move;
+    }
+    .chartview-content {
+      canvas {
         cursor: move;
       }
-      .chartview-content {
-        canvas {
-          cursor: move;
+      .richtexteditor {
+        .w-e-text-container {
+          pointer-events: all;
+        }
+      }
+    }
+  }
+  .previewbox {
+    top: 50px;
+    width: 98% !important;
+    height: 80%;
+    .ant-modal-content {
+      height: 100%;
+      .ant-modal-close {
+        display: none;
+      }
+      .ant-modal-body {
+        height: 100%;
+        .chartview {
+          height: 100%;
         }
-        .richtexteditor {
-          .w-e-text-container {
-            pointer-events: all;
+        .chartview-edit {
+          .chartview-toolbar {
+            cursor: default;
           }
-          &:hover {
-            .w-e-toolbar {
-              display: flex;
-            }
+          .chartview-content {
+            canvas {
+              cursor: default;
+            } 
           }
         }
       }
     }
   }
+  .react-grid-layout {
+    background: #eee;
+  }
   .layoutJSON {
     background: #ddd;
     border: 1px solid black;

+ 1 - 1
src/components/datasource/columnConfig.jsx

@@ -1,5 +1,5 @@
 import React from 'react'
-import { Form, Input, Button, Select, Table, Checkbox, Divider, Icon, Popconfirm, Switch } from 'antd'
+import { Form, Input, Button, Select, Table, Checkbox, Divider, Icon, Popconfirm } from 'antd'
 import { connect } from 'dva'
 import COLUMN_TYPE from './columnType.json'
 import { Resizable } from 'react-resizable';

+ 28 - 1
src/models/dashboard.js

@@ -105,7 +105,7 @@ export default {
                 let body = {
                     bdName: title,
                     bdNote: '',
-                    configuration: JSON.stringify(items),
+                    bdConfiguration: JSON.stringify(items),
                     thumbnail: '',
                     createBy: 'zhuth'
                 }
@@ -156,6 +156,33 @@ export default {
                 message.error('保存失败');
             }
         },
+        *remoteDelete(action, { select, call, put }) {
+            const dashboard = yield select(state => state.present.dashboard);
+            const code = action.code;
+            let list = dashboard.list;
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.DASHBOARD_DELETE,
+                    body: [code]
+                });
+                console.log('删除看板', [code], res);
+                if(!res.err && res.data.code > 0) {
+                    for(let i = 0; i < list.length; i++) {
+                        if(list[i].code === code) {
+                            list.splice(i, 1);
+                            break;
+                        }
+                    }
+                    yield put({ type: 'list', list: list });
+                    message.success('删除成功');
+                }else {
+                    message.error('删除失败: ' + (res.err || res.data.msg));
+                }
+            }catch(e) {
+                console.log(e);
+                message.error('删除失败');
+            }
+        },
     },
     subscriptions: {
         setup({ dispatch, history}) {