Browse Source

数据源编辑界面以及交互事件完善

zhuth 7 years ago
parent
commit
eaf6e4d035

+ 1 - 1
app/components/chartDesigner/header.jsx

@@ -33,7 +33,7 @@ const Header = ({ chartDesigner, dispatch }) => {
             <div className='header-item toolbar-buttons'>
                 <div className=''>
                     <Button onClick={() => {
-                        dispatch({ type: 'chartDesigner/a'});
+                        dispatch({ type: 'chartDesigner/fetchChartData'});
                     }}>请求测试</Button>
                     <Button className='button-uodo' icon='undo' onClick={() => {
                         dispatch(ActionCreators.undo());

+ 2 - 1
app/components/chartDesigner/sections/barConfigForm.jsx

@@ -49,6 +49,7 @@ const barConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
 					options={columns.filter(c =>['ordinal', 'categorical', 'time'].indexOf(c.type) != -1).map((c, i)=>{
 						
 						return {
+							type: c.type,
 							value: c.name,
 							label: c.label,
 							children: granularity[c.type]
@@ -58,7 +59,7 @@ const barConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
 						let column = {};
 						let granularity = {};
 						if(value.length > 0) {
-							column = { value: items[0].value, label: items[0].label };
+							column = { type: items[0].type, value: items[0].value, label: items[0].label };
 						}
 						if(items.length > 1) {
 							granularity = { value: items[1].value, label: items[1].label };

+ 0 - 2
app/components/chartDesigner/sections/filterBox.jsx

@@ -43,7 +43,6 @@ class FilterBox extends React.Component {
     }
 
     componentWillUnmount() {
-        console.log('unmount');
     }
 
     componentWillReceiveProps() {
@@ -207,7 +206,6 @@ class FilterBox extends React.Component {
             return (
                 <Row key={`filterDiv[${key}]`}>
                     <Col span={22}>
-                        {}
                         <Col span={6}>
                             <FormItem key={key}>
                                 {getFieldDecorator(`filterName${key}`, {

+ 0 - 0
app/components/datasource/columnBox.jsx


+ 67 - 0
app/components/datasource/dataSource.less

@@ -11,4 +11,71 @@
             }
         }
     }
+    .datasource-tab {
+        .datasource-table {
+            .ant-table {
+                margin-top: 5px;
+                table {
+                    padding: 8px;
+                    .ant-table-row {
+                        td {
+                            padding: 8px;
+                            .datasource-name {
+                                display: flex;
+                                .datasource-type {
+                                    width: 20px;
+                                    height: 20px;
+                                    background-size: cover;
+                                    background-repeat: no-repeat;
+                                    background-image: url('https://test-feapp.oss-cn-beijing.aliyuncs.com/feapp/s70f_180613_fix_a_t/images/trdservices/44_2.png');
+                                }
+                                .type-oracle {
+                                    background-position: 0 -731px;
+                                }
+                            }
+                            .datasource-tag {
+                                margin: 2px;
+                                cursor: default;
+                            }
+                            .ant-dropdown-trigger {
+                                font-size: 18px;
+                                cursor: pointer;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+.operationmenu {
+    padding: 0;
+    width: 120px;
+    .ant-dropdown-menu-item-divider {
+        margin: 0;
+    }
+}
+
+.newdatasource-box {
+    top: 80px;
+    height: 512px;
+    width: 600px !important;
+    .ant-modal-content {
+        height: 100%;
+        .ant-modal-body {
+            padding: 6px;
+            max-height: 382px;
+            overflow-y: auto;
+            .textarea-target {
+                margin-top: 4px;
+                margin-bottom: 3px;
+            }
+            .textarea-desc {
+                margin-top: 4px;
+            }
+        }
+        .ant-modal-footer {
+            height: 32px;
+        }
+    }
 }

+ 199 - 0
app/components/datasource/dataSourceBox.jsx

@@ -0,0 +1,199 @@
+import React from 'react'
+import { Modal, Form, Row, Col, Input, Select, Icon, Menu, Dropdown } from 'antd'
+const FormItem = Form.Item
+const SelectOption = Select.Option
+const OptionGroup = Select.OptGroup
+const InputGroup = Input.Group
+const SubMenu = Menu.SubMenu
+const MenuItem = Menu.Item
+const MenuItemGroup = Menu.ItemGroup;
+import { connect } from 'dva'
+import dataSource from '../../models/dataSource'
+
+const DataSourceBox = ({operation, dispatch, dataSource, visibleBox, hideBox, form}) => {
+    const formItemLayout = {
+        labelCol: { span: 4 },
+        wrapperCol: { span: 20 },
+    };
+
+    const okHandler = () => {
+        if(operation == 'create') {
+            dispatch({ type: 'dataSource/addDataSource' });
+        }else if(operation == 'edit') {
+            dispatch({ type: 'dataSource/modifyDataSource' });
+        }
+        hideBox();
+    }
+
+    const dataSourceLinkMenu = (
+        <Menu
+            className='menu-datasource-link'
+            onClick={() => {
+                dispatch({ type: 'dataSource/setNewModelField', name: 'address', value: '1111adddd' });
+                dispatch({ type: 'dataSource/setNewModelField', name: 'port', value: '1234' });
+                dispatch({ type: 'dataSource/setNewModelField', name: 'type', value: {
+                    key: 'oracle',
+                    label: 'ORACLE'
+                } });
+                dispatch({ type: 'dataSource/setNewModelField', name: 'dbName', value: 'orcl' });
+                dispatch({ type: 'dataSource/setNewModelField', name: 'userName', value: 'UAS' });
+                dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: 'select!#%*(' });
+            }}
+        >
+            <MenuItem>1111</MenuItem>
+            <MenuItem>2222</MenuItem>
+            <MenuItem>33333</MenuItem>
+            <MenuItem>44</MenuItem>
+        </Menu>
+    );
+
+    return (
+        <Modal
+            className='newdatasource-box'
+            title={`${operation=='create'?'新增':'修改'}数据源配置`}
+            visible={visibleBox}
+            onOk={() => {okHandler()}}
+            onCancel={hideBox}
+            maskClosable={false}
+            destroyOnClose={true}
+        >
+            <Form size='small'>
+                <FormItem label='数据源名称' {...formItemLayout}>
+                    <Input
+                        value={dataSource.newOne.name}
+                        onChange={(e) => { dispatch({ type: 'dataSource/setNewModelField', name: 'name', value: e.target.value }) }}>
+                    </Input>
+                </FormItem>
+                <Row>
+                    <Col span={19}>
+                        <FormItem label='数据库地址' {...{
+                            labelCol: { span: 5 },
+                            wrapperCol: { span: 19 }
+                        }}>
+                            <Input
+                                value={dataSource.newOne.address}
+                                onChange={(e) => {
+                                    dispatch({ type: 'dataSource/setNewModelField', name: 'address', value: e.target.value });
+                                }}
+                                addonBefore={
+                                    <Dropdown
+                                        trigger={['click']}
+                                        overlay={dataSourceLinkMenu}
+                                    >
+                                        <div style={{cursor: 'pointer'}}>导入<Icon type='down' /></div>
+                                    </Dropdown>
+                                }
+                            />
+                        </FormItem>
+                    </Col>
+                    <Col span={5}>
+                        <FormItem label='端口' {...{
+                            labelCol: { span: 12 },
+                            wrapperCol: { span: 12 }
+                        }}>
+                            <Input
+                                value={dataSource.newOne.port}
+                                onChange={(e) => {
+                                    dispatch({ type: 'dataSource/setNewModelField', name: 'port', value: e.target.value });
+                                }}
+                            />
+                        </FormItem>
+                    </Col>
+                </Row>
+                <FormItem label='数据库类型' {...formItemLayout}>
+                    <Select
+                        value={dataSource.newOne.type}
+                        labelInValue={true}
+                        onChange={(value) => {
+                            dispatch({ type: 'dataSource/setNewModelField', name: 'type', value: value} );
+                        }}
+                    >
+                        <SelectOption value='oracle'>
+                            ORACLE
+                        </SelectOption>
+                        <SelectOption value='mysql'>
+                            MYSQL
+                        </SelectOption>
+                        <SelectOption value='sqlserver'>
+                            SQLSERVER
+                        </SelectOption>
+                        <SelectOption value='sqlite'>
+                            SQLITE
+                        </SelectOption>
+                    </Select>
+                </FormItem>
+                <FormItem label='数据库名' {...formItemLayout}>
+                    <Input
+                        value={dataSource.newOne.dbName}
+                        onChange={(e) => {
+                            dispatch({ type: 'dataSource/setNewModelField', name: 'dbName', value: e.target.value });
+                        }}
+                    />
+                </FormItem>
+                <Row>
+                    <Col span={12}>
+                        <FormItem label='用户名' {...{
+                            labelCol: { span: 8 },
+                            wrapperCol: { span: 16 }
+                        }}>
+                            <Input
+                                value={dataSource.newOne.userName}
+                                onChange={(e) => {
+                                    dispatch({ type: 'dataSource/setNewModelField', name: 'userName', value: e.target.value });
+                                }}
+                            />
+                        </FormItem>
+                    </Col>
+                    <Col span={12}>
+                        <FormItem label='密码' {...{
+                            labelCol: { span: 8 },
+                            wrapperCol: { span: 16 }
+                        }}>
+                            <Input
+                                type='password'
+                                value={dataSource.newOne.password}
+                                onChange={(e) => {
+                                    dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: e.target.value });
+                                }}
+                            />
+                        </FormItem>
+                    </Col>
+                </Row>
+                <FormItem className='textarea-target' label='加载对象' {...formItemLayout}>
+                    <Input.TextArea
+                        placeholder='输入表名或查询SQL'
+                        autosize={{ minRows: 3 }}
+                    />
+                </FormItem>
+                <FormItem label='自定义标签' {...formItemLayout}>
+                    <Select
+                        mode="tags"
+                        placeholder='多个标签使用逗号或空格分隔'
+                        tokenSeparators={[',', ' ']}
+                        value={dataSource.newOne.tags}
+                        dropdownStyle={{display: 'none'}}
+                        onChange={(value) => {
+                            dispatch({ type: 'dataSource/setNewModelField', name: 'tags', value: value });
+                        }}
+                    >
+                    </Select>
+                </FormItem>
+                <FormItem className='textarea-desc' label='说明' {...formItemLayout}>
+                    <Input.TextArea
+                        autosize={{ minRows: 2, maxRows: 2 }}
+                        value={dataSource.newOne.description}
+                        onChange={(e) => {
+                            dispatch({ type: 'dataSource/setNewModelField', name: 'description', value: e.target.value });
+                        }}
+                    />
+                </FormItem>
+            </Form>
+        </Modal>
+    )
+}
+
+function mapStateToProps({ present: { dataSource } }) {
+    return { dataSource: dataSource };
+}
+
+export default connect(mapStateToProps)(DataSourceBox)

+ 130 - 23
app/components/datasource/datasource.jsx

@@ -1,29 +1,90 @@
 import React from 'react'
-import { Tabs, Input, Button, Table, Icon, Divider } from 'antd'
+import { Tabs, Input, Button, Table, Icon, Tag, Menu, Dropdown } from 'antd'
 const { TabPane } = Tabs
 const { Search } = Input
+import DataSourceBox from './dataSourceBox'
+import { connect } from 'dva'
+import dataSource from '../../models/dataSource'
 import './dataSource.less'
 
 class DataSource extends React.Component {
     constructor(props) {
         super(props);
         this.state = {
-
+            operation: 'create', // 打开数据编辑界面的类型
+            visibleBox: false, // 数据编辑界面显示标识
+            selectedCode: -1 // 当前选中的table行code
         }
     };
+
+    showDataSourceBox = (o) => {
+        this.setState({
+            operation: o,
+            visibleBox: true
+        });
+    }
+
+    hideBox = () => {
+        this.setState({
+            visibleBox: false
+        });
+    }
+
     render() {
+        
+        const { dataSource, dispatch } = this.props;
+        const { visibleBox, operation, selectedCode } = this.state;
+        
+        console.log(dataSource.list);
+        const moreOperatingMenu = (
+            <Menu className='operationmenu'>
+                <Menu.Item
+                    onClick={(e) => {
+                        let selectedModel = dataSource.list.find((i) => { return i.code == selectedCode })
+                        dispatch({ type: 'dataSource/setNewModel', model: selectedModel });
+                        this.showDataSourceBox('edit');
+                    }}>
+                    属性设置
+                </Menu.Item>
+                <Menu.Item
+                    onClick={(e) => {
+                        let selectedModel = dataSource.list.find((i) => { return i.code == selectedCode })
+                        this.showColumnBox();
+                    }}>
+                    数据列设置
+                </Menu.Item>
+                <Menu.Item>预览数据</Menu.Item>
+                <Menu.Divider />
+                <Menu.Item>删除</Menu.Item>
+            </Menu>
+        );
         const columns = [{
             title: '名称',
             dataIndex: 'name',
-            key: 'name'
+            key: 'name',
+            render: (text, record) => {
+                return <div className='datasource-name'>
+                    <div className={`datasource-type type-${record.type.key}`}></div>
+                    <div>{text}</div>
+                </div>
+            }
         }, {
             title: '标签',
-            dataIndex: 'tag',
+            dataIndex: 'tags',
             key: 'tag',
+            render: (text, record) => {
+                text=text.join(',');
+                let tags = text ? text.split(',').map((t, i) => {
+                    return <Tag className='datasource-tag' key={i}>{t}</Tag>
+                }) : '';
+                return (<div>
+                    {tags}
+                </div>)
+            }
         }, {
             title: '说明',
             dataIndex: 'description',
-            key: 'description',
+            key: 'description'
         }, {
             title: '创建人',
             dataIndex: 'creator',
@@ -31,22 +92,41 @@ class DataSource extends React.Component {
         }, {
             title: '创建时间',
             dataIndex: 'createTime',
-            key: 'createTime'
+            key: 'createTime',
+            render: (text, record) => text.format('yyyy-MM-dd hh:mm:ss')
+        }, {
+            title: '图表',
+            dataIndex: 'chartSum',
+            key: 'chartSum'
         }, {
             title: '操作',
             key: 'action',
-            render: (text, record) => (
-                <span>
-                    <a href="javascript:;">Action 一 {record.name}</a>
-                    <Divider type="vertical" />
-                    <a href="javascript:;">Delete</a>
-                    <Divider type="vertical" />
-                    <a href="javascript:;" className="ant-dropdown-link">
-                        More actions <Icon type="down" />
-                    </a>
-                </span>
+            render: (text, record, index) => (
+                <Dropdown code={record.code} overlay={moreOperatingMenu} trigger={['click']} >
+                    <Icon type="setting" />
+                </Dropdown>
             ),
         }];
+        const data = [{
+            key: '1',
+            name: '销售数据',
+            type: 'oracle',
+            tag: '销售,常常常常文本',
+            description: '这是一个测试数据',
+            creator: 'zhuth',
+            createTime: new Date('2018-07-01 08:33:18').format('yyyy-MM-dd hh:mm:ss'),
+            chartSum: 9
+        }, {
+            key: '2',
+            name: '采购数据',
+            type: 'sqlserver',
+            tag: '采购',
+            description: '这是一个测试数据',
+            creator: 'zhuth',
+            createTime: new Date('2018-07-04 10:38:29').format('yyyy-MM-dd hh:mm:ss'),
+            chartSum: 7
+        }];
+
         return (
             <Tabs
                 className='datasource-tabs'
@@ -55,18 +135,41 @@ class DataSource extends React.Component {
                 tabBarExtraContent={
                     <div className='datasource-tabs-tools'>
                         <Search
-                            placeholder="input search text"
+                            placeholder="请输入关键字"
                             onSearch={value => console.log(value)}
                         />
-                        <Button><Icon type="plus" />添加数据源</Button>
+                        <Button onClick={() => {
+                            dispatch({ type: 'dataSource/resetNewModel' });
+                            this.showDataSourceBox('create')}
+                        }>
+                            <Icon type="plus" />添加数据源
+                        </Button>
+                        <DataSourceBox operation={operation} visibleBox={visibleBox} hideBox={this.hideBox} />
                     </div>
                 }
             >
-                <TabPane tab="公共数据源" key="1" >
-                    <Table columns={columns} dataSource={[]} />
+                <TabPane className='datasource-tab public-datasource' tab="公共数据源" key="1" >
+                    <Table
+                        className='datasource-table public-datasource-table'
+                        columns={columns}
+                        dataSource={dataSource.list}
+                        loading={false}
+                        size='small'
+                        onRow={(record) => {
+                            return {
+                                onClick: () => {this.setState({ selectedCode:  record.code})}
+                            }
+                        }}
+                    />
                 </TabPane>
-                <TabPane tab="我的数据源" key="2" >
-                    {/* CSV/XLS组件 */}
+                <TabPane className='datasource-tab my-datasource' tab="我的数据源" key="2" >
+                    <Table
+                        className='datasource-table my-datasource-table'
+                        columns={columns}
+                        dataSource={dataSource.my}
+                        loading={false}
+                        size='small'
+                    />
                 </TabPane>
             </Tabs>
         )
@@ -75,4 +178,8 @@ class DataSource extends React.Component {
     }
 }
 
-export default DataSource
+function mapStateToProps({present: {dataSource}}) {
+    return { dataSource }
+}
+
+export default connect(mapStateToProps)(DataSource)

+ 7 - 5
app/index.js

@@ -1,8 +1,9 @@
-import dva from 'dva';
-import undoable from 'redux-undo';
+import dva from 'dva'
+import undoable from 'redux-undo'
 import chartDesigner from './models/chartDesigner'
-import './utils/baseUtils';
-import './index.less';
+import dataSource from './models/dataSource'
+import './utils/baseUtils'
+import './index.less'
 
 // 1. Initialize
 const app = dva({
@@ -16,7 +17,8 @@ const app = dva({
 // app.use({});
 
 // 3. Model
-app.model(chartDesigner);
+app.model(chartDesigner); // 图表
+app.model(dataSource); // 数据源
 
 // 4. Router
 app.router(require('./routes/router'));

+ 23 - 15
app/models/chartDesigner.js

@@ -108,7 +108,7 @@ export default {
         }
     },
     effects: {
-        *['a'](action, { select, call, put }) {
+        *['fetchChartData'](action, { select, call, put }) {
             const chartDesigner = yield select(state => state.present.chartDesigner);
             const { barConfig, preparing } = chartDesigner;
             const res = yield call(service.fetch, {
@@ -116,28 +116,36 @@ export default {
                 body: {
                     "tableName": "TEST_BI_DATA",
                     "groups": preparing.groupBy.map(g => g.key),
-                    "xAxis": barConfig.xAxis.column.value,
-                    "yAxis": barConfig.yAxis.column.value,
-                    "dataType": barConfig.yAxis.gauge.value
+                    "xAxis": {
+                        "columnRename": barConfig.xAxis.column.value,
+                        "columnType": barConfig.xAxis.column.type,
+                        "dataType": barConfig.xAxis.granularity.value
+                    },
+                    "yAxis": {
+                        "columnRename": barConfig.yAxis.column.value,
+                        "dataType": barConfig.yAxis.gauge.value
+                    }
                 }
             });
 
             console.log({
-                url: URLS.CHART_BAR_OPTION,
-                body: {
-                    "tableName": "TEST_BI_DATA",
-                    "groups": preparing.groupBy.map(g => g.key),
-                    "xAxis": barConfig.xAxis.column.value,
-                    "yAxis": barConfig.yAxis.column.value,
+                "tableName": "TEST_BI_DATA",
+                "groups": preparing.groupBy.map(g => g.key),
+                "xAxis": {
+                    "columnRename": barConfig.xAxis.column.value,
+                    "columnType": barConfig.xAxis.column.type,
+                    "dataType": barConfig.xAxis.granularity.value
+                },
+                "yAxis": {
+                    "columnRename": barConfig.yAxis.column.value,
                     "dataType": barConfig.yAxis.gauge.value
                 }
             })
-            res.viewType = 'bar';
-            res.data.data.xTitle = barConfig.xAxis?barConfig.xAxis.column.label:null
-            res.data.data.yTitle = barConfig.yAxis?barConfig.yAxis.column.label:null;
-            res.data.data.gauge = barConfig.yAxis?barConfig.yAxis.gauge.label:null;
-            console.log(res);
             if(!res.err && res.data.code > 0) {
+                res.viewType = 'bar';
+                res.data.data.xTitle = barConfig.xAxis?`${barConfig.xAxis.column.label}${barConfig.xAxis.granularity.value?'('+barConfig.xAxis.granularity.label+')':''}`:null
+                res.data.data.yTitle = barConfig.yAxis?barConfig.yAxis.column.label:null;
+                res.data.data.gauge = barConfig.yAxis?barConfig.yAxis.gauge.label:null;
                 yield put({ type: 'setModel', name: 'chartOption', value: res });
             }else {
                 yield put({ type: 'setModel', name: 'chartOption', value: {} });

+ 75 - 0
app/models/dataSource.js

@@ -0,0 +1,75 @@
+export default {
+    namespace: 'dataSource',
+    state: {
+        origin: { type:{key:'',label:''}, address:'', port:'', dbName:'', table:[], userName:'', password:'', desc:'' },
+        newOne: {},
+        list: [{
+            key: 'c1',
+            code: 'd0001',
+            name: '1',
+            type: {key: 'oracle', label: 'ORACLE'},
+            address: '2',
+            port: '3',
+            table: ['aa', 'b'],
+            creator: 'zhuth',
+            createTime: new Date(),
+            userName: '2222',
+            password: 'aaaww',
+            tags: ['tttt', 'accc'],
+            desc: ' dddddddddddddddddddddd'
+        }, {
+            key: 'c2',
+            code: 'd0002',
+            name: 'aldjalsdal',
+            type: {key: 'oracle', label: 'ORACLE'},
+            address: '2',
+            port: '3',
+            table: ['aa', 'b'],
+            creator: 'zhuth',
+            createTime: new Date(),
+            userName: '2222',
+            password: 'aaaww',
+            tags: ['tttt', 'accc'],
+            desc: ' dddddddddddddddddddddd'
+        }],
+        public: [],
+        my: []
+    },
+    reducers: {
+        addDataSource(state, action) {
+            let newOne = Object.assign({}, state.newOne);
+            let list = state.list;
+            newOne.key = new Date().getMilliseconds()+(Math.random()*100).toFixed(0);
+            newOne.code = new Date().getMilliseconds()+(Math.random()*100).toFixed(0);
+            newOne.creator = 'zhuth';
+            newOne.createTime = new Date();
+            list.push(newOne);
+            return Object.assign({}, state, {list});
+        },
+        modifyDataSource(state, action) {
+            const { newOne } = state;
+            let list = state.list;
+            for(let i = 0; i < list.length; i++) {
+                if(list[i].code == newOne.code) {
+                    list[i] = Object.assign({}, newOne);
+                    break;
+                }
+            }
+            return Object.assign({}, state, {list});
+        },
+        setNewModelField(state, action) {
+            const { name, value } = action;
+            let newOne = state.newOne;
+            newOne[name] = value;
+            return Object.assign({}, state, {newOne});
+        },
+        setNewModel(state, action) {
+            const { model } = action;
+            let newOne = Object.assign({}, model);
+            return Object.assign({}, state, {newOne});
+        },
+        resetNewModel(state, action) {
+            return Object.assign({}, state, {newOne: {}});
+        }
+    }
+};