Commit 5c6f8d22 authored by wenmo's avatar wenmo

BI

parent 9f3e81e2
......@@ -67,6 +67,9 @@ Dinky(原 Dlink):
| | | 新增 自动注册 Yarn 创建的集群 | 0.4.0 |
| | SQL | 新增 外部数据源的 SQL 校验 | 0.5.0 |
| | | 新增 外部数据源的 SQL 执行与预览 | 0.5.0 |
| | BI | 新增 折线图的渲染 | 0.5.0 |
| | | 新增 条形图图的渲染 | 0.5.0 |
| | | 新增 饼图的渲染 | 0.5.0 |
| | 元数据 | 新增 查询外部数据源的元数据信息 | 0.4.0 |
| | 归档 | 新增 执行与提交历史 | 0.4.0 |
| 运维中心 | 暂无 | 暂无 | 0.4.0 |
......
......@@ -24,7 +24,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <scope>test</scope>-->
<scope>test</scope>
</dependency>
</dependencies>
</project>
@import '~antd/es/style/themes/default.less';
.form_setting{
padding-left: 10px;
}
.form_item{
margin-bottom: 5px;
}
import {Button, Tag, Row, Col, Form, Select, Empty, Switch} from "antd";
import {StateType} from "@/pages/FlinkSqlStudio/model";
import {connect} from "umi";
import styles from "./index.less";
import {FireOutlined, SearchOutlined, RedoOutlined, InfoCircleOutlined} from '@ant-design/icons';
import {useEffect, useState} from "react";
import React from "react";
const {Option} = Select;
export type BarChartConfig = {
isGroup: boolean,
isStack: boolean,
isPercent: boolean,
xField: string,
yField: string,
seriesField?: string,
label?: { },
};
export type BarChartProps = {
onChange: (values: Partial<BarChartConfig>) => void;
data: [];
column: [];
};
const BarChartSetting: React.FC<BarChartProps> = (props) => {
const {current,column,onChange: handleChange,dispatch} = props;
const [form] = Form.useForm();
useEffect(() => {
form.setFieldsValue(current.console.chart);
}, [current.console.chart]);
const onValuesChange = (change: any, all: any) => {
let config: BarChartConfig = {
isGroup: all.isGroup,
isStack: all.isStack,
isPercent: all.isPercent,
xField: all.xField?all.xField:column[0],
yField: all.yField?all.yField:column.length>1?column[1]:column[0],
label: {
position: 'middle',
content: (item) => {
return item[all.xField];
},
style: {
fill: '#fff',
},
},
};
if(all.seriesField){
config.seriesField = all.seriesField;
}
handleChange(config);
};
const getColumnOptions = () => {
const itemList = [];
for (const item of column) {
itemList.push(<Option key={item} value={item} label={item}>
{item}
</Option>)
}
return itemList;
};
return (
<>
<Form
form={form}
className={styles.form_setting}
onValuesChange={onValuesChange}
>
<Row>
<Col span={12}>
<Form.Item
label="x 轴" className={styles.form_item} name="xField"
>
{column&&column.length > 0 ? (
<Select allowClear showSearch
defaultValue={column[0]} value={column[0]}>
{getColumnOptions()}
</Select>):(<Select allowClear showSearch>
{column&&getColumnOptions()}
</Select>)}
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="y 轴" className={styles.form_item} name="yField"
>
{column&&column.length > 1 ? (
<Select allowClear showSearch
defaultValue={column[1]} value={column[1]}>
{getColumnOptions()}
</Select>):(<Select allowClear showSearch>
{column&&getColumnOptions()}
</Select>)}
</Form.Item>
</Col>
</Row>
<Row>
<Col span={12}>
<Form.Item
label="分组字段" className={styles.form_item} name="seriesField"
>
{column&&column.length > 0 ? (
<Select allowClear showSearch>
{getColumnOptions()}
</Select>):(<Select allowClear showSearch>
{column&&getColumnOptions()}
</Select>)}
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="分组" className={styles.form_item} name="isGroup" valuePropName="checked"
>
<Switch checkedChildren="启用" unCheckedChildren="禁用"
/>
</Form.Item>
</Col>
</Row>
<Row>
<Col span={12}>
<Form.Item
label="堆叠" className={styles.form_item} name="isStack" valuePropName="checked"
>
<Switch checkedChildren="启用" unCheckedChildren="禁用"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="百分比" className={styles.form_item} name="isPercent" valuePropName="checked"
>
<Switch checkedChildren="启用" unCheckedChildren="禁用"
/>
</Form.Item>
</Col>
</Row>
</Form>
</>
);
};
export default connect(({ Studio }: { Studio: StateType }) => ({
current: Studio.current,
result: Studio.result,
}))(BarChartSetting);
import {Button, Tag,Row, Col,Form,Select, Empty} from "antd";
import {Button, Tag, Row, Col, Form, Select, Empty, Switch} from "antd";
import {StateType} from "@/pages/FlinkSqlStudio/model";
import {connect} from "umi";
import styles from "./index.less";
import {FireOutlined, SearchOutlined,RedoOutlined} from '@ant-design/icons';
import {useState} from "react";
import {FireOutlined, SearchOutlined, RedoOutlined, InfoCircleOutlined} from '@ant-design/icons';
import {useEffect, useState} from "react";
import React from "react";
const {Option} = Select;
export type LineChartConfig = {
padding: string,
xField: string,
yField: string,
seriesField?: string,
stepType?: string,
xAxis?: {
type?: string,
},
slider?: {},
};
export type LineChartProps = {
......@@ -25,10 +28,33 @@ export type LineChartProps = {
const LineChartSetting: React.FC<LineChartProps> = (props) => {
const {data,column,onChange: handleChange,dispatch} = props;
const {current,column,onChange: handleChange,dispatch} = props;
const [form] = Form.useForm();
useEffect(() => {
form.setFieldsValue(current.console.chart);
}, [current.console.chart]);
const onValuesChange = (change: any, all: any) => {
handleChange(all);
let config: LineChartConfig = {
padding: 'auto',
xField: all.xField?all.xField:column[0],
yField: all.yField?all.yField:column.length>1?column[1]:column[0],
};
if(all.seriesField){
config.seriesField = all.seriesField;
}
if(all.openStepType){
config.stepType = 'hv';
}
if(all.openSlider){
config.slider = {
start: 0,
end: 0.5,
};
}
handleChange(config);
};
const getColumnOptions = () => {
......@@ -44,6 +70,7 @@ const LineChartSetting: React.FC<LineChartProps> = (props) => {
return (
<>
<Form
form={form}
className={styles.form_setting}
onValuesChange={onValuesChange}
>
......@@ -53,9 +80,10 @@ const LineChartSetting: React.FC<LineChartProps> = (props) => {
label="x 轴" className={styles.form_item} name="xField"
>
{column&&column.length > 0 ? (
<Select defaultValue={column[0]} value={column[0]}>
<Select allowClear showSearch
defaultValue={column[0]} value={column[0]}>
{getColumnOptions()}
</Select>):(<Select >
</Select>):(<Select allowClear showSearch>
{column&&getColumnOptions()}
</Select>)}
</Form.Item>
......@@ -64,10 +92,11 @@ const LineChartSetting: React.FC<LineChartProps> = (props) => {
<Form.Item
label="y 轴" className={styles.form_item} name="yField"
>
{column&&column.length > 0 ? (
<Select defaultValue={column[0]} value={column[0]}>
{column&&column.length > 1 ? (
<Select allowClear showSearch
defaultValue={column[1]} value={column[1]}>
{getColumnOptions()}
</Select>):(<Select >
</Select>):(<Select allowClear showSearch>
{column&&getColumnOptions()}
</Select>)}
</Form.Item>
......@@ -79,13 +108,31 @@ const LineChartSetting: React.FC<LineChartProps> = (props) => {
label="分组字段" className={styles.form_item} name="seriesField"
>
{column&&column.length > 0 ? (
<Select defaultValue={column[0]} value={column[0]}>
<Select allowClear showSearch>
{getColumnOptions()}
</Select>):(<Select >
</Select>):(<Select allowClear showSearch>
{column&&getColumnOptions()}
</Select>)}
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="缩略轴" className={styles.form_item} name="openSlider" valuePropName="checked"
>
<Switch checkedChildren="启用" unCheckedChildren="禁用"
/>
</Form.Item>
</Col>
</Row>
<Row>
<Col span={12}>
<Form.Item
label="阶梯线" className={styles.form_item} name="openStepType" valuePropName="checked"
>
<Switch checkedChildren="启用" unCheckedChildren="禁用"
/>
</Form.Item>
</Col>
</Row>
</Form>
</>
......
@import '~antd/es/style/themes/default.less';
.form_setting{
padding-left: 10px;
}
.form_item{
margin-bottom: 5px;
}
import {Button, Tag, Row, Col, Form, Select, Empty, Switch} from "antd";
import {StateType} from "@/pages/FlinkSqlStudio/model";
import {connect} from "umi";
import styles from "./index.less";
import {FireOutlined, SearchOutlined, RedoOutlined, InfoCircleOutlined} from '@ant-design/icons';
import {useEffect, useState} from "react";
import React from "react";
const {Option} = Select;
export type PieChartConfig = {
angleField: string,
colorField: string,
label: {},
interactions: [],
};
export type PieChartProps = {
onChange: (values: Partial<PieChartConfig>) => void;
data: [];
column: [];
};
const PieChartSetting: React.FC<PieChartProps> = (props) => {
const {current,column,onChange: handleChange,dispatch} = props;
const [form] = Form.useForm();
useEffect(() => {
form.setFieldsValue(current.console.chart);
}, [current.console.chart]);
const onValuesChange = (change: any, all: any) => {
let config: PieChartConfig = {
angleField: all.angleField?all.angleField:column[0],
colorField: all.colorField?all.colorField:column.length>1?column[1]:column[0],
label: {
type: 'inner',
offset: '-30%',
content: ({ percent }) => `${(percent * 100).toFixed(0)}%`,
style: {
fontSize: 14,
textAlign: 'center',
},
},
interactions: [
{
type: 'element-active',
},
],
};
handleChange(config);
};
const getColumnOptions = () => {
const itemList = [];
for (const item of column) {
itemList.push(<Option key={item} value={item} label={item}>
{item}
</Option>)
}
return itemList;
};
return (
<>
<Form
form={form}
className={styles.form_setting}
onValuesChange={onValuesChange}
>
<Row>
<Col span={12}>
<Form.Item
label="弧轴" className={styles.form_item} name="angleField"
>
{column&&column.length > 0 ? (
<Select allowClear showSearch
defaultValue={column[0]} value={column[0]}>
{getColumnOptions()}
</Select>):(<Select allowClear showSearch>
{column&&getColumnOptions()}
</Select>)}
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="颜色" className={styles.form_item} name="colorField"
>
{column&&column.length > 1 ? (
<Select allowClear showSearch
defaultValue={column[1]} value={column[1]}>
{getColumnOptions()}
</Select>):(<Select allowClear showSearch>
{column&&getColumnOptions()}
</Select>)}
</Form.Item>
</Col>
</Row>
</Form>
</>
);
};
export default connect(({ Studio }: { Studio: StateType }) => ({
current: Studio.current,
result: Studio.result,
}))(PieChartSetting);
......@@ -2,40 +2,32 @@ import {Button, Tag,Row, Col,Form,Select, Empty} from "antd";
import {StateType} from "@/pages/FlinkSqlStudio/model";
import {connect} from "umi";
import styles from "./index.less";
import {FireOutlined, SearchOutlined,RedoOutlined} from '@ant-design/icons';
import {RedoOutlined} from '@ant-design/icons';
import {CHART, isSql} from "@/components/Studio/conf";
import { Line } from '@ant-design/plots';
import {useEffect, useState} from "react";
import { Line,Bar,Pie } from '@ant-design/plots';
import React, {useEffect, useState} from "react";
import LineChartSetting from "./LineChartSetting";
import BarChartSetting from "./BarChartSetting";
import PieChartSetting from "./PieChartSetting";
import {showJobData} from "@/components/Studio/StudioEvent/DQL";
import {Dispatch} from "@@/plugin-dva/connect";
const {Option} = Select;
const Chart = (props:any) => {
const {current,result,dispatch} = props;
const {current,result,height,dispatch} = props;
const [config, setConfig] = useState({});
const [data, setData] = useState([]);
const [column, setColumn] = useState([]);
const [type, setType] = useState<string>(CHART.LINE);
const [form] = Form.useForm();
useEffect(() => {
toBuild();
}, [result,current.console.result]);
const toBuild = () => {
if(isSql(current.task.dialect)){
setData(current.console.result.result.rowData);
setColumn(current.console.result.result.columns);
}else{
setData(result.rowData);
setColumn(result.columns);
}
};
form.setFieldsValue(current.console.chart);
}, [current.console.chart]);
const toRebuild = () => {
if(!isSql(current.task.diagnosticCodesToIgnore)){
if(!isSql(current.task.dialect)){
showJobData(current.console.result.jobId,dispatch);
}
};
......@@ -43,29 +35,55 @@ const Chart = (props:any) => {
const onValuesChange = (change: any, all: any) => {
if(change.type){
setType(change.type);
props.saveChart({type:change.type});
}
};
const renderChartSetting = () => {
if(!current.console.chart||!current.console.result.result){
return undefined;
}
switch (type){
case CHART.LINE:
return <LineChartSetting data={data} column={column} onChange={(value) => {
return <LineChartSetting column={current.console.result.result.columns} onChange={(value) => {
setConfig(value);
props.saveChart({...value,type: current.console.chart.type});
}} />;
case CHART.BAR:
return <BarChartSetting column={current.console.result.result.columns} onChange={(value) => {
setConfig(value);
props.saveChart({...value,type: current.console.chart.type});
}} />;
case CHART.PIE:
return <PieChartSetting column={current.console.result.result.columns} onChange={(value) => {
setConfig(value);
props.saveChart({...value,type: current.console.chart.type});
}} />;
default:
return <LineChartSetting />;
return <LineChartSetting column={current.console.result.result.columns} onChange={(value) => {
setConfig(value);
props.saveChart({...value,type: current.console.chart.type});
}} />
}
};
const renderChartContent = () => {
if(column.length==0){
if(!current.console.result.result||!current.console.result.result.columns){
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />;
}
switch (type){
switch (current.console.chart.type){
case CHART.LINE:
return <Line data={data} {...config} />;
return <Line data={current.console.result.result.rowData} {...config} />;
case CHART.BAR:
return <Bar data={current.console.result.result.rowData} {...config} />;
case CHART.PIE:
if(config.angleField){
return <Pie data={current.console.result.result.rowData} {...config} />;
} else {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />;
}
default:
return <Line data={data} {...config} />;
return <Line data={current.console.result.result.rowData} {...config} />;
}
};
......@@ -77,6 +95,7 @@ const Chart = (props:any) => {
</Col>
<Col span={8}>
<Form
form={form}
className={styles.form_setting}
onValuesChange={onValuesChange}
>
......@@ -86,15 +105,17 @@ const Chart = (props:any) => {
label="图形类型" className={styles.form_item} name="type"
>
<Select defaultValue={CHART.LINE} value={CHART.LINE}>
<Option value={CHART.LINE}>折线图</Option>
<Option value={CHART.LINE}>{CHART.LINE}</Option>
<Option value={CHART.BAR}>{CHART.BAR}</Option>
<Option value={CHART.PIE}>{CHART.PIE}</Option>
</Select>
</Form.Item>
</Col>
<Col span={12}>
{ !isSql(current.task.dialect) ? <Col span={12}>
<Button type="primary" onClick={toRebuild} icon={<RedoOutlined />}>
重新渲染
刷新数据
</Button>
</Col>
</Col>:undefined}
</Row>
</Form>
{renderChartSetting()}
......@@ -104,7 +125,14 @@ const Chart = (props:any) => {
);
};
const mapDispatchToProps = (dispatch: Dispatch)=>({
saveChart:(chart: any)=>dispatch({
type: "Studio/saveChart",
payload: chart,
}),
})
export default connect(({ Studio }: { Studio: StateType }) => ({
current: Studio.current,
result: Studio.result,
}))(Chart);
}),mapDispatchToProps)(Chart);
......@@ -12,6 +12,7 @@ import {
import styles from "./index.less";
import {showJobData} from "@/components/Studio/StudioEvent/DQL";
import StudioPreview from "../StudioPreview";
import {getJobData} from "@/pages/FlinkSqlStudio/service";
const { Title, Paragraph, Text, Link } = Typography;
......@@ -61,14 +62,19 @@ const StudioHistory = (props: any) => {
const [row, setRow] = useState<HistoryItem>();
const [config,setConfig] = useState<HistoryConfig>();
const [type,setType] = useState<number>();
const [result,setResult] = useState<{}>();
const showDetail=(row:HistoryItem,type:number)=>{
setRow(row);
setModalVisit(true);
setType(type);
setConfig(JSON.parse(row.config));
if(type==3){
showJobData(row.jobId,dispatch)
if(type===3){
// showJobData(row.jobId,dispatch)
const res = getJobData(row.jobId);
res.then((resd)=>{
setResult(resd.datas);
});
}
};
......@@ -341,7 +347,7 @@ const StudioHistory = (props: any) => {
</Tag>
</ProDescriptions.Item>
<ProDescriptions.Item span={2} >
<StudioPreview style={{width: '100%'}}/>
<StudioPreview result={result} style={{width: '100%'}}/>
</ProDescriptions.Item>
</ProDescriptions>
)}
......
......@@ -34,5 +34,5 @@ const StudioPreview = (props:any) => {
export default connect(({ Studio }: { Studio: StateType }) => ({
current: Studio.current,
result: Studio.result,
// result: Studio.result,
}))(StudioPreview);
......@@ -32,8 +32,8 @@ const StudioTable = (props:any) => {
{current.console.result.jobId && (<Tag color="blue" key={current.console.result.jobId}>
<FireOutlined /> {current.console.result.jobId}
</Tag>)}
{result.columns?
<DTable dataSource={result.rowData} columns={getColumns(result.columns)}/>
{current.console.result.result&&current.console.result.result.columns?
<DTable dataSource={current.console.result.result.rowData} columns={getColumns(current.console.result.result.columns)}/>
:(<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />)
}
</>)
......
......@@ -55,13 +55,13 @@ const StudioConsole = (props: any) => {
tab={
<span>
<BarChartOutlined />
统计
BI
</span>
}
key="StudioChart"
>
<Scrollbars style={{height: consoleHeight}}>
<Chart/>
<Chart height={consoleHeight} />
</Scrollbars>
</TabPane>
<TabPane
......
import {getJobData} from "@/pages/FlinkSqlStudio/service";
export function showJobData(jobId:string,dispatch:any) {
if(!jobId){
return;
}
const res = getJobData(jobId);
res.then((result)=>{
dispatch&&dispatch({
......
......@@ -161,7 +161,8 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
...result.datas,
},
console:{
result:[],
result: {},
chart: {},
},
monaco: React.createRef(),
};
......
......@@ -21,7 +21,9 @@ export const DIALECT = {
};
export const CHART = {
LINE:'Line',
LINE:'折线图',
BAR:'条形图',
PIE:'饼图',
};
export const isSql = (dialect: string)=>{
......
......@@ -96,6 +96,7 @@ export type TaskType = {
export type ConsoleType = {
result: {};
chart: {};
}
export type TabsItemType = {
......@@ -185,6 +186,7 @@ export type ModelType = {
saveClusterConfiguration: Reducer<StateType>;
saveDataBase: Reducer<StateType>;
saveEnv: Reducer<StateType>;
saveChart: Reducer<StateType>;
};
};
......@@ -239,6 +241,7 @@ const Model: ModelType = {
},
console: {
result: {},
chart: {},
},
monaco: {},
sqlMetaData: undefined,
......@@ -285,6 +288,7 @@ const Model: ModelType = {
},
console: {
result: {},
chart: {},
},
monaco: {},
sqlMetaData: undefined,
......@@ -510,11 +514,25 @@ const Model: ModelType = {
};
},
saveResult(state, {payload}) {
// return {
// ...state,
// result: {
// ...payload
// },
// };
let newTabs = state?.tabs;
let newCurrent = state?.current;
for (let i = 0; i < newTabs.panes.length; i++) {
if (newTabs.panes[i].key === newTabs.activeKey) {
newTabs.panes[i].console.result.result = payload;
newCurrent = newTabs.panes[i];
break;
}
}
return {
...state,
result: {
...payload
},
current: newCurrent,
tabs: newTabs,
};
},
saveCluster(state, {payload}) {
......@@ -542,6 +560,21 @@ const Model: ModelType = {
...state,
env: payload,
};
},saveChart(state, {payload}) {
let newTabs = state?.tabs;
let newCurrent = state?.current;
for (let i = 0; i < newTabs.panes.length; i++) {
if (newTabs.panes[i].key === newTabs.activeKey) {
newTabs.panes[i].console.chart = payload;
newCurrent = newTabs.panes[i];
break;
}
}
return {
...state,
current: newCurrent,
tabs: newTabs,
};
},
},
};
......
......@@ -554,7 +554,7 @@ export default (): React.ReactNode => {
<Link>新增 编辑器选项卡右键关闭其他和关闭所有</Link>
</li>
<li>
<Link>新增 统计选项卡的图标支持</Link>
<Link>新增 BI选项卡的折线图、条形图、饼图</Link>
</li>
</ul>
</Paragraph>
......
......@@ -105,6 +105,9 @@ Dinky 通过已注册的集群配置来获取对应的 YarnClient 实例。对
| | | 新增 自动注册 Yarn 创建的集群 | 0.4.0 |
| | SQL | 新增 外部数据源的 SQL 校验 | 0.5.0 |
| | | 新增 外部数据源的 SQL 执行与预览 | 0.5.0 |
| | BI | 新增 折线图的渲染 | 0.5.0 |
| | | 新增 条形图图的渲染 | 0.5.0 |
| | | 新增 饼图的渲染 | 0.5.0 |
| | 元数据 | 新增 查询外部数据源的元数据信息 | 0.4.0 |
| | 归档 | 新增 执行与提交历史 | 0.4.0 |
| 运维中心 | 暂无 | 暂无 | 0.4.0 |
......
......@@ -105,6 +105,9 @@ Dinky 通过已注册的集群配置来获取对应的 YarnClient 实例。对
| | | 新增 自动注册 Yarn 创建的集群 | 0.4.0 |
| | SQL | 新增 外部数据源的 SQL 校验 | 0.5.0 |
| | | 新增 外部数据源的 SQL 执行与预览 | 0.5.0 |
| | BI | 新增 折线图的渲染 | 0.5.0 |
| | | 新增 条形图图的渲染 | 0.5.0 |
| | | 新增 饼图的渲染 | 0.5.0 |
| | 元数据 | 新增 查询外部数据源的元数据信息 | 0.4.0 |
| | 归档 | 新增 执行与提交历史 | 0.4.0 |
| 运维中心 | 暂无 | 暂无 | 0.4.0 |
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment