Browse Source

图表样式控制初步设计/报表列表隐藏备注字段/分组名称校验逻辑

zhuth 6 years ago
parent
commit
df4481c0a9

+ 1 - 0
package.json

@@ -25,6 +25,7 @@
     "moment": "^2.22.2",
     "prop-types": "^15.6.2",
     "qrcode.react": "^0.9.3",
+    "rc-color-picker": "^1.2.6",
     "react": "^16.2.0",
     "react-dom": "^16.2.0",
     "react-grid-layout": "^0.16.6",

+ 58 - 37
src/components/chartDesigner/sections/style/bar.jsx

@@ -1,58 +1,79 @@
 import React from 'react'
 import { connect } from 'dva'
-import { Collapse, 
-    // Menu,
-    Form, Checkbox, Select, Radio } from 'antd'
-// const SubMenu = Menu.SubMenu;
-// const MenuItemGroup = Menu.ItemGroup;
+import { Collapse, Input, Form, Checkbox, Select, Radio } from 'antd'
+import ColorPicker from '../../../common/colorPicker/index'
+import './bar.less'
 
 class BarStyle extends React.Component {
     
     constructor(props) {
         super(props);
         this.state = {
-            layoutColumn: null,
-            formatColumn: null,
-            colorColumn: null,
+            formItemLayout: {
+                labelCol: { span: 8 },
+                wrapperCol: { span: 16 },
+            },
+            backgroundColor: '#ffffff', // 背景
+            markTextColor: '#eeeeee', // 标签文字
+            colors: [
+                "#c23531",
+                "#2f4554",
+                "#61a0a8",
+                "#d48265",
+                "#91c7ae",
+                "#749f83",
+                "#ca8622",
+                "#bda29a",
+                "#6e7074",
+                "#546570",
+                "#c4ccd3"
+            ], // 主题
         }
     }
 
     render() {
-        const { chartDesigner, formItemLayout, dispatch } = this.props;
-        // const { layoutColumn, formatColumn, colorColumn } = this.state;
+        const { formItemLayout, backgroundColor, markTextColor, colors } = this.state;
+        const { chartDesigner, dispatch } = this.props;
         const { styleConfig } = chartDesigner;
-        const { bar } = (styleConfig || {  });
+        const bar = styleConfig.bar || {};
 
         return (
-            <Collapse defaultActiveKey={['chart', 'legend', 'color']}>
-                <Collapse.Panel className='chart' header='图形' key='chart'>
+            <Collapse className='collapse-bar-style' defaultActiveKey={['chart', 'legend', 'color']}>
+                <Collapse.Panel className='chart' header='基本配置' key='chart'>
                     <Form>
-                        <Form.Item label='类型' {...formItemLayout}>
-                            <Select defaultValue='default' onChange={(value) => {
-                                const stack = value === 'stack';
-                                dispatch({ type: 'chartDesigner/setField', name: 'styleConfig', value: { ...styleConfig, bar: { ...bar, stack }} });
-                            }}>
-                                <Select.Option value='default'>标准</Select.Option>
-                                <Select.Option value='stack'>堆叠</Select.Option>
-                            </Select>
-                        </Form.Item>
-                        <Form.Item label='显示ToolTip' {...formItemLayout}>
-                            <Checkbox onChange={(e) => {
-                                const checked = e.target.checked;
-                                dispatch({ type: 'chartDesigner/setField', name: 'styleConfig', value: { ...styleConfig, bar: { ...bar, visibleToolTip: checked }} });
+                        <Form.Item label='背景' {...formItemLayout}>
+                            <ColorPicker placeholder={'#ffffff'} value={backgroundColor} onChange={v => {
+                                this.setState({
+                                    backgroundColor: v
+                                }, () => {
+                                    window.clearTimeout(this.backgroundColorKey);
+                                    this.backgroundColorKey = window.setTimeout(() => {
+                                        dispatch({ type: 'chartDesigner/setField', name: 'styleConfig', value: {
+                                            ...styleConfig,
+                                            bar: { ...bar, backgroundColor: v }
+                                        } });
+                                    }, 200);
+                                });
                             }}/>
                         </Form.Item>
-                        <Form.Item label='显示标签' {...formItemLayout}>
-                            <div>
-                                <Checkbox defaultChecked={false} onChange={(e) => {
-                                    // const checked = e.target.checked;
-                                    // dispatch({ type: 'chartDesigner/setField', name: 'styleConfig', value: { ...styleConfig, table: { ...table, bordered: checked }} });
-                                }}/>
-                                <Select onChange={null} >
-                                    <Select.Option value='left'>内部</Select.Option>
-                                    <Select.Option value='center'>外部</Select.Option>
-                                </Select>
-                            </div>
+                        <Form.Item label='主题' {...formItemLayout}>
+                            { colors.map((c, i) => <ColorPicker key={i} value={colors[i]} onChange={v => {
+                                colors[i] = v
+                                this.setState({
+                                    colors,
+                                }, () => {
+                                    window.clearTimeout(this['colors-' + i + '-key']);
+                                    this['colors-' + i + '-key'] = window.setTimeout(() => {
+                                        dispatch({ type: 'chartDesigner/setField', name: 'styleConfig', value: {
+                                            ...styleConfig,
+                                            bar: { ...bar, colors: colors }
+                                        } });
+                                    }, 200);
+                                });
+                            }}/>) }
+                            <div class="colors-control"><a onClick={() => {
+                                
+                            }}>增加</a><a>减少</a></div>
                         </Form.Item>
                     </Form>
                 </Collapse.Panel>

+ 26 - 0
src/components/chartDesigner/sections/style/bar.less

@@ -0,0 +1,26 @@
+.collapse-bar-style {
+    @tab-bg-color-active: #727D99;
+    @text-color: #D2E0F3;
+    
+    border: none;
+    >.ant-collapse-item {
+        >.ant-collapse-header {
+
+        }
+        >.ant-collapse-content {
+            >.ant-collapse-content-box {
+                background: @tab-bg-color-active;
+                .colors-control {
+                    a {
+                        cursor: pointer;
+                        color: @text-color;
+                        margin: 0px 0 0 5px;
+                        &:hover {
+                            text-decoration: underline;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 1 - 1
src/components/chartDesigner/sections/style/index.jsx

@@ -11,7 +11,7 @@ export default ({ viewType }) => {
     if(viewType === 'dataView') {
         // styleForm = <TableStyle formItemLayout={formItemLayout}/>
     }else if(viewType === 'bar') {
-        styleForm = <BarStyle formItemLayout={formItemLayout}/>
+        // styleForm = <BarStyle />
     }
     return styleForm;
 }

+ 46 - 0
src/components/common/colorPicker/index.jsx

@@ -0,0 +1,46 @@
+import React from 'react'
+import { Input } from 'antd'
+import { RGBToHex, HexToRGB } from '../../../utils/baseUtils'
+import ColorPicker from 'rc-color-picker'
+import 'rc-color-picker/assets/index.css'
+import './index.less'
+
+class InputColorPicker extends React.Component {
+    render() {
+        const { placeholder, value, onChange } = this.props;
+        let defaultColorHex, defaultAlpha, colorHex, alpha;
+
+        if((value + '').match(/^#/)) {
+            if(value.length > 3) {
+                colorHex = value;
+            }
+            alpha = 100;
+        }else if(!!(value + '').match(/^rgb[a]{0,1}\(/i)) {
+            let arr = value.split(/[(,)]/),
+                len = arr.length,
+                colorRGB = 'RGB(' + (Number((arr[1] || '').trim()) || 0) + ',' + (Number((arr[2] || '').trim()) || 0) + ',' + (Number((arr[3] || '').trim()) || 0) + ',' + (Number((arr[4] || '').trim()) || 1) * 100 + ')'
+            colorHex = RGBToHex(colorRGB);
+            alpha = (Number((arr[4] || '').trim()) || 1) * 100;
+        }
+        return <div className='colorpicker-wrap'>
+            <Input ref={node => this.inputRef = node} placeholder={placeholder} value={value} onChange={e => {
+                typeof onChange === 'function' && onChange(e.target.value);
+            }}/>
+            <ColorPicker
+                animation="slide-up"
+                color={colorHex}
+                alpha={alpha}
+                onChange={(colors) => {
+                    const { alpha, color: colorHex, open } = colors;
+                    let color = colorHex;
+                    if(alpha !== 100) {
+                        color = HexToRGB(colorHex, alpha)
+                    }
+                    this.inputRef.value = color;
+                    typeof onChange === 'function' && onChange(color)
+                }}
+            />
+        </div> 
+    }
+}
+export default InputColorPicker

+ 19 - 0
src/components/common/colorPicker/index.less

@@ -0,0 +1,19 @@
+.colorpicker-wrap {
+    input {
+        width: 80%;
+        border-radius: @border-radius-base 0 0 @border-radius-base;
+        border-right: none;
+    }
+    .rc-color-picker-wrap  {
+        position: absolute;
+        span {
+            height: 32px;
+            width: 32px; 
+            display: block;
+            position: relative;
+            top: 4px;
+            border-color: #ccc;
+            border-radius: 0 @border-radius-base @border-radius-base 0;
+        }
+    }
+}

+ 78 - 24
src/components/common/groupManageMentBox/box.jsx

@@ -1,5 +1,5 @@
 import React from 'react'
-import { Modal, Tree, Input, Icon, Row, Col, Button } from 'antd'
+import { Modal, Tree, Form, Input, Icon, Row, Col, Button } from 'antd'
 import DeleteBox from '../deleteBox/deleteBox'
 import { arrayToTree, hashcode } from '../../../utils/baseUtils'
 import './box.less'
@@ -20,7 +20,8 @@ class GroupBox extends React.Component {
             mGroups: [], // 修改的分组
             dGroups: [], // 删除的分组
             willDeleteGroup: null,
-            visibleDeleteGroupBox: false
+            visibleDeleteGroupBox: false,
+            validInfo: {},
         }
     }
 
@@ -43,7 +44,7 @@ class GroupBox extends React.Component {
     }
 
     generateTreeNode(treeData) {
-        const { filterLabel, editingKey } = this.state;
+        const { filterLabel, editingKey, validInfo } = this.state;
         const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
         const regLabel = filterLabel.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
         let arr = this.onFilter(treeData, regLabel);
@@ -61,27 +62,54 @@ class GroupBox extends React.Component {
             return <TreeNode
                 title={<div className='node-title'>
                     {t.code === editingKey ? <div className='input'>
-                        <Input
-                            ref={(input) => {this['inputRef-' + t.code] = input}}
-                            size="small"
-                            defaultValue={t.label}
-                            addonAfter={<Icon style={{ cursor: 'pointer', color: '#52C41A' }} type="check-circle" onClick={() => {
-                                this.setState({ editingKey: false });
-                            }}/>}
-                            onBlur={(e) => {
-                                this.setState({ editingKey: false });
-                                if(!e.target.value.trim()) {
-                                    return;
-                                }
-                                this.modifyGroup({ ...t, label:e.target.value });
-                            }} onPressEnter={(e) => {
-                                this.setState({ editingKey: false });
-                                if(!e.target.value.trim()) {
-                                    return;
-                                }
-                                this.modifyGroup({ ...t, label:e.target.value });
-                            }}
-                        />
+                        <Form.Item
+                            status={(validInfo['inputRef-' + t.code] || { status: 'success' }).status}
+                            help={(validInfo['inputRef-' + t.code] || { help: '' }).help}
+                        >
+                            <Input
+                                ref={(input) => {this['inputRef-' + t.code] = input}}
+                                size="small"
+                                defaultValue={t.label}
+                                addonAfter={<Icon style={{ cursor: 'pointer', color: '#52C41A' }} type="check-circle" onClick={() => {
+                                    this.setState({ editingKey: false });
+                                }}/>}
+                                onChange={e => {
+                                    let val = e.target.value + '';
+                                    let status, help;
+                                    if(val.trim().length === 0) {
+                                        status = 'error';
+                                        help = '*分组名称不能为空';
+                                    }else if(val.trim().length > 20) {
+                                        status = 'error';
+                                        help = '*分组名称不能超过20个字符';
+                                    }else {
+                                        status = 'success';
+                                        help = '';
+                                    }
+                                    window.clearTimeout(this['inputRef-' + t.code + '-key']);
+                                    this['inputRef-' + t.code + '-key'] = window.setTimeout(() => {
+                                        let obj = {};
+                                        obj['inputRef-' + t.code] = { status, help }
+                                        this.setState({
+                                            validInfo: { ...validInfo, ...obj }
+                                        });
+                                    }, 100);
+                                }}
+                                onBlur={(e) => {
+                                    this.setState({ editingKey: false });
+                                    if(!e.target.value.trim()) {
+                                        return;
+                                    }
+                                    this.modifyGroup({ ...t, label:e.target.value });
+                                }} onPressEnter={(e) => {
+                                    this.setState({ editingKey: false });
+                                    if(!e.target.value.trim()) {
+                                        return;
+                                    }
+                                    this.modifyGroup({ ...t, label:e.target.value });
+                                }}
+                            />
+                        </Form.Item>
                     </div> :
                     <div className='label' onDoubleClick={() => {
                         this.setState({
@@ -90,6 +118,8 @@ class GroupBox extends React.Component {
                             this['inputRef-' + t.code].focus();
                         });
                     }}>{title}</div>}
+                    { t.code !== editingKey && t.label.trim().length === 0 && <span style={{ position: 'absolute', color: 'red', marginTop: '14px', fontSize: '12px' }}>*分组名称不能为空</span> }
+                    { t.code !== editingKey && t.label.length > 20 && <span style={{ position: 'absolute', color: 'red', marginTop: '14px', fontSize: '12px' }}>*分组名称不能超过20个字符</span> }
                     <div className='tools'>
                         {t.code !== editingKey && <Icon type='edit' onClick={() => {
                             this.setState({
@@ -276,6 +306,18 @@ class GroupBox extends React.Component {
         }
     }
 
+    checkValid = () => {
+        const { validInfo } = this.state;
+        let flag = true;
+        for(let k in validInfo) {
+            if(validInfo[k].status === 'error') {
+                flag = false;
+                break;
+            }
+        }
+        return flag;
+    }
+
     render() {
         let { visibleBox, hideBox, okHandler } = this.props;
         let { groupData, expandedKeys, selectedGroup, autoExpandParent, aGroups, mGroups, dGroups, visibleDeleteGroupBox, willDeleteGroup } = this.state;
@@ -284,6 +326,18 @@ class GroupBox extends React.Component {
             className='groupmanagement-box'
             title={'分组维护'}
             visible={visibleBox}
+            footer={
+                <div>
+                    <Button onClick={hideBox}>取 消</Button>
+                    <Button disabled={!this.checkValid()} type='primary' onClick={() => {
+                        if(aGroups.length === 0 && mGroups.length === 0 && dGroups.length === 0) {
+                            hideBox()
+                        }else {
+                            okHandler(aGroups, mGroups, dGroups);
+                        }
+                    }}>确 定</Button>
+                </div>
+            }
             onOk={() => {
                 okHandler(aGroups, mGroups, dGroups);
             }}

+ 8 - 0
src/components/common/groupManageMentBox/box.less

@@ -9,6 +9,14 @@
     }
     .ant-tree li .ant-tree-node-content-wrapper {
         width: ~'calc(100% - 24px)';
+
+        .ant-form-explain {
+            position: absolute;
+            margin-left: 212px;
+            margin-top: -38px;
+            font-size: 12px;
+            color: red;
+        }
     }
     .ant-tree-title {
         cursor: default;

+ 22 - 22
src/components/dashboard/list.jsx

@@ -269,28 +269,28 @@ class DashboardList extends React.Component {
                     </span>
                 )
             }
-        }, {
-            title: '备注',
-            dataIndex: 'description',
-            key: 'description',
-            width: 200,
-            onCell: () => {
-                return {
-                    style: {
-                        whiteSpace: 'nowrap',
-                        maxWidth: 200,
-                    }
-                }
-            },
-            render: (text) => <EllipsisTooltip title={text}>{
-                filterLabel && filterItem.name === 'description' ? ((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
-            }</EllipsisTooltip>,
+        // }, {
+            // title: '备注',
+            // dataIndex: 'description',
+            // key: 'description',
+            // width: 200,
+            // onCell: () => {
+            //     return {
+            //         style: {
+            //             whiteSpace: 'nowrap',
+            //             maxWidth: 200,
+            //         }
+            //     }
+            // },
+            // render: (text) => <EllipsisTooltip title={text}>{
+            //     filterLabel && filterItem.name === 'description' ? ((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
+            // }</EllipsisTooltip>,
         }, {
             title: '创建人',
             dataIndex: 'creatorName',

+ 1 - 1
src/models/chartDesigner.js

@@ -57,7 +57,7 @@ export default {
             lineConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, threshold: 1000 },
             pieConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, threshold: 20 },
             scatterConfig: { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} }, groupBy: {key:''}, threshold: 1000 },
-            styleConfig: { visibleIndex: true },
+            styleConfig: { bar: { backgroundColor: '#ffffff' } },
             otherConfig:{},
             description: '',
             filters: [],

+ 1 - 1
src/models/dashboard.js

@@ -26,7 +26,7 @@ export default {
             filterLabel: '',
             filterItems: [ // 可选过滤字段
                 { name: 'name', label: '报表名称', type: 'string' },
-                { name: 'description', label: '备注', type: 'string' },
+                // { name: 'description', label: '备注', type: 'string' },
                 { name: 'creatorName', label: '创建人', type: 'string' },
                 { name: 'createTime', label: '创建时间', type: 'date' },
             ],

+ 17 - 1
src/utils/baseUtils.js

@@ -188,4 +188,20 @@ function ArrayEquals(arr1, arr2) {
     return true;
 }
 
-export { remove, isEqual, getUrlParam, hashcode, delay, dateFormat, arrayToTree, ArrayEquals, deepAssign };
+function HexToRGB(hex, alpha) {
+    var hex = hex.replace('#', '0x'),
+        r = hex >> 16,
+        g = hex >> 8 & 0xff,
+        b = hex & 0xff;
+    return 'rgb' + (alpha ? 'a' : '') + '(' + r + ',' + g + ',' + b + (alpha ? (',' + alpha/100) : '') + ')';
+}
+
+function RGBToHex(rgb) {
+    var rgbArr = rgb.split(/[(,)]/),
+        color = rgbArr[1] << 16 | rgbArr[2] << 8 | rgbArr[3];
+    return '#' + color.toString(16).padStart(6, '0');
+}
+
+export { remove, isEqual, getUrlParam, hashcode, delay, dateFormat, arrayToTree, ArrayEquals, deepAssign,
+    HexToRGB, RGBToHex
+};