fix:Dashboard

This commit is contained in:
shanhexi
2023-11-30 14:57:16 +08:00
parent 886ec8a4fc
commit 56211a37ed
11 changed files with 137 additions and 142 deletions

View File

@@ -36,6 +36,7 @@
"DBAI",
"dbhub",
"Dmaven",
"echart",
"echarts",
"favicons",
"findstr",

View File

@@ -85,13 +85,22 @@ const TreeNode = memo((props: TreeNodeIProps) => {
},
refresh: _props?.refresh || false,
})
.then((res) => {
if (res.length) {
.then((res: any) => {
if (res.length || res.data) {
setTimeout(() => {
setTreeNodeData({
...treeNodeData,
children: res,
});
console.log(res);
if (res.data) {
setTreeNodeData({
...treeNodeData,
children: res.data,
total: res.total,
});
} else {
setTreeNodeData({
...treeNodeData,
children: res,
});
}
setIsLoading(false);
}, 200);
} else {
@@ -274,11 +283,21 @@ const TreeNode = memo((props: TreeNodeIProps) => {
);
}, [isFocus, isLoading, rightClickMenu]);
const sectionHeight = useMemo(() => {
console.log(treeNodeData.total);
if (treeNodeData.total) {
return `${treeNodeData.total * 26}px`;
} else {
return 'auto';
}
}, [treeNodeData.total]);
return (
<>
<div style={{ height: sectionHeight }}>
{(showTreeNode || showParentNode) && treeNodeDom}
{treeNodes}
</>
</div>
);
});

View File

@@ -261,13 +261,12 @@ export const treeConfig: { [key in TreeNodeType]: ITreeConfigItem } = {
},
};
});
r(tableList);
// {
// data: tableList,
// pageNo: res.pageNo,
// pageSize: res.pageSize,
// total: res.total,
// }
r({
data: tableList,
pageNo: res.pageNo,
pageSize: res.pageSize,
total: res.total,
} as any);
})
.catch((error) => {
j(error);
@@ -276,7 +275,7 @@ export const treeConfig: { [key in TreeNodeType]: ITreeConfigItem } = {
},
operationColumn: [
OperationColumn.CreateConsole,
OperationColumn.ViewAllTable,
// OperationColumn.ViewAllTable,
OperationColumn.CreateTable,
OperationColumn.Refresh,
],

View File

@@ -190,12 +190,14 @@ const SelectBoundInfo = memo((props: IProps) => {
databaseName: void 0,
schemaName: void 0,
});
historyService.updateSavedConsole({
id: boundInfo.consoleId,
dataSourceId: currentData.value,
dataSourceName: currentData.label,
type: currentData.type,
});
if (boundInfo.consoleId) {
historyService.updateSavedConsole({
id: boundInfo.consoleId,
dataSourceId: currentData.value,
dataSourceName: currentData.label,
type: currentData.type,
});
}
};
// 选择数据库
@@ -208,10 +210,12 @@ const SelectBoundInfo = memo((props: IProps) => {
schemaName: void 0,
});
historyService.updateSavedConsole({
id: boundInfo.consoleId,
databaseName: _databaseName,
});
if (boundInfo.consoleId) {
historyService.updateSavedConsole({
id: boundInfo.consoleId,
databaseName: _databaseName,
});
}
};
// 选择schema
@@ -222,10 +226,12 @@ const SelectBoundInfo = memo((props: IProps) => {
schemaName: _schemaName,
});
historyService.updateSavedConsole({
id: boundInfo.consoleId,
schemaName: _schemaName,
});
if (boundInfo.consoleId) {
historyService.updateSavedConsole({
id: boundInfo.consoleId,
schemaName: _schemaName,
});
}
};
const getAllTableNameList = (dataSourceId, databaseName, schemaName?) => {

View File

@@ -9,7 +9,7 @@ import { getCookie } from '@/utils';
interface IProps {
isActive?: boolean;
source: string;
source?: string;
editorRef: any;
boundInfo: any;
defaultValue?: string;

View File

@@ -48,7 +48,7 @@ export type IAppendValue = {
};
export interface IBoundInfo {
consoleId: number;
consoleId?: number;
dataSourceId: number;
dataSourceName: string;
databaseType: DatabaseTypeCode;
@@ -59,7 +59,7 @@ export interface IBoundInfo {
interface IProps {
/** 调用来源 */
source: 'workspace';
source?: 'workspace';
isActive: boolean;
/** 添加或修改的内容 */
appendValue?: IAppendValue;

View File

@@ -1,7 +1,6 @@
.container {
flex: 1;
position: relative;
// border: 1px solid var(--color-gray-300);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-l-g);
padding: 16px;
@@ -16,9 +15,6 @@
font-size: 14px;
}
.title {
}
.edit {
cursor: pointer;
}
@@ -117,24 +113,26 @@
}
.editBlock {
border-radius: var(--border-radius-l-g);
background-color: var(--color-bg-subtle);
max-height: 600px;
border-radius: 4px;
// background-color: var(--color-bg-subtle);
max-height: 1000px;
}
.editorBlock {
display: flex;
border-bottom: 1px solid var(--color-border-secondary);
// flex-direction: column;
flex-wrap: wrap;
}
.editor {
flex: 2;
min-width: 320px;
max-height: 320px;
min-height: 320px;
display: flex;
flex-direction: column;
position: relative;
border: 1px solid var(--color-border-secondary);
overflow: hidden;
}
.dataSourceSelect {
@@ -144,10 +142,11 @@
}
.chartParamsForm {
border: 1px solid var(--color-border);
border-left: 1px solid var(--color-border);
color: var(--color-text);
flex: 1;
min-width: 120px;
border-left: 1px solid var(--color-border-secondary);
display: flex;
flex-direction: column;
justify-content: start;
@@ -163,5 +162,7 @@
}
.editorOptionBlock {
padding: 12px;
display: flex;
justify-content: flex-end;
padding: 20px 0px 0px 0px;
}

View File

@@ -1,4 +1,4 @@
import { IChartItem, IChartType, IConnectionDetails, ITreeNode } from '@/typings';
import { IChartItem, IChartType, IConnectionDetails } from '@/typings';
import React, { useEffect, useRef, useState, useMemo } from 'react';
import styles from './index.less';
import addImage from '@/assets/img/add.svg';
@@ -7,7 +7,7 @@ import Line from '../chart/line';
import Pie from '../chart/pie';
import Bar from '../chart/bar';
import { MoreOutlined } from '@ant-design/icons';
import { Button, Cascader, Dropdown, Form, message, Select, Spin } from 'antd';
import { Button, Dropdown, Form, message, Select, Spin } from 'antd';
import { deleteChart, getChartById, updateChart } from '@/service/dashboard';
import ConsoleEditor from '@/components/ConsoleEditor';
import Iconfont from '@/components/Iconfont';
@@ -15,9 +15,6 @@ import sqlService, { IExecuteSqlParams } from '@/service/sql';
import { Option } from '@/typings/common';
import { handleDatabaseAndSchema } from '@/utils/database';
import i18n from '@/i18n';
import { useTheme } from '@/hooks';
import { EditorThemeType, ThemeType } from '@/constants';
import { IRemainingUse } from '@/typings/ai';
import { isValid } from '@/utils/check';
const handleSQLResult2ChartData = (data) => {
@@ -54,9 +51,8 @@ interface IChartItemProps {
id: number;
isEditing?: boolean;
canAddRowItem: boolean;
connectionList: IConnectionDetails[];
tableList: ITreeNode[];
remainingUse: IRemainingUse;
connectionList: any[];
remainingUse: any;
onDelete?: (id: number) => void;
addChartTop?: () => void;
addChartBottom?: () => void;
@@ -65,20 +61,17 @@ interface IChartItemProps {
}
function ChartItem(props: IChartItemProps) {
const { connectionList, tableList, remainingUse } = props;
const { connectionList, id } = props;
const [cascaderOption, setCascaderOption] = useState<Option[]>([]);
const [curConnection, setCurConnection] = useState<IConnectionDetails>();
const [chartData, setChartData] = useState<IChartItem>({});
const [chartMetaData, setChartMetaData] = useState<any>();
const [cascaderValue, setCascaderValue] = useState<(string | number)[]>([]);
const [, setCascaderValue] = useState<(string | number)[]>([]);
const [isEditing, setIsEditing] = useState<boolean>(props.isEditing ?? false);
const [isLoading, setIsLoading] = useState(false);
const [initDDL, setInitDDL] = useState('');
const [form] = Form.useForm(); // 创建一个表单实例
const chartRef = useRef<any>();
const [appTheme] = useTheme();
const { id } = props;
useEffect(() => {
if (id !== undefined) {
@@ -116,13 +109,6 @@ function ChartItem(props: IChartItemProps) {
handleChartConfigChange();
}, [chartData.sqlData]);
const loadData = (selectedOptions: any) => {
// 只选择了dataSource
const dataSourceId = selectedOptions[0].value;
const dataSource = connectionList.find((c) => c.id === dataSourceId);
setCurConnection(dataSource);
};
const queryDatabaseAndSchemaList = async (dataSourceId: number) => {
const res = await sqlService.getDatabaseSchemaList({ dataSourceId });
const dataSource = (cascaderOption || []).find((c) => c.value === dataSourceId);
@@ -132,28 +118,28 @@ function ChartItem(props: IChartItemProps) {
setCascaderOption([...cascaderOption]);
};
const handleExecuteSQL = async (sql: string, chartData: IChartItem) => {
const { dataSourceId, databaseName } = chartData;
const handleExecuteSQL = async (sql: string, _chartData: IChartItem) => {
const { dataSourceId, databaseName } = _chartData;
if (!isValid(dataSourceId)) {
message.success(i18n('dashboard.editor.execute.noDataSource'));
return;
}
setIsLoading(true);
try {
let executeSQLParams: IExecuteSqlParams = {
const executeSQLParams: IExecuteSqlParams = {
sql,
dataSourceId,
databaseName,
};
// 获取当前SQL的查询结果
let sqlResult = await sqlService.executeSql(executeSQLParams);
const sqlResult = await sqlService.executeSql(executeSQLParams);
let sqlData;
if (sqlResult && sqlResult[0]) {
sqlData = handleSQLResult2ChartData(sqlResult[0]);
}
setChartData({
...chartData,
..._chartData,
ddl: sql,
sqlData,
});
@@ -167,14 +153,13 @@ function ChartItem(props: IChartItemProps) {
const queryChartData = async () => {
setIsLoading(true);
const { id } = props;
let res = await getChartById({ id });
const res = await getChartById({ id: props.id });
setChartData(res);
// 设置级联value
const cascaderKey = ['dataSourceId', 'databaseName', 'schemaName'];
const cascaderValue = cascaderKey.map((k: string) => res[k]).filter((i) => !!i);
setCascaderValue(cascaderValue);
const _cascaderValue = cascaderKey.map((k: string) => res[k]).filter((i) => !!i);
setCascaderValue(_cascaderValue);
// 设置Chart参数eg ChartType、xAxis、yAxis
const formValue = JSON.parse(res.schema || '{}');
@@ -206,22 +191,22 @@ function ChartItem(props: IChartItemProps) {
const onExport2Image = () => {
const echartInstance = chartRef.current.getEchartsInstance();
let img = new Image();
const img = new Image();
img.src = echartInstance.getDataURL({
type: 'png',
devicePixelRatio: 4,
backgroundColor: '#FFF',
});
img.onload = function () {
let canvas = document.createElement('canvas');
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
let ctx = canvas.getContext('2d');
const ctx = canvas.getContext('2d');
ctx?.drawImage(img, 0, 0);
let dataURL = canvas.toDataURL('image/png');
const dataURL = canvas.toDataURL('image/png');
var a = document.createElement('a');
let event = new MouseEvent('click');
const a = document.createElement('a');
const event = new MouseEvent('click');
a.download = 'image.png';
a.href = dataURL;
a.dispatchEvent(event);
@@ -229,9 +214,8 @@ function ChartItem(props: IChartItemProps) {
};
const onDeleteChart = () => {
const { id } = props;
deleteChart({ id });
props.onDelete && props.onDelete(id);
deleteChart({ id: props.id });
props.onDelete && props.onDelete(props.id);
};
const handleSaveChart = async () => {
@@ -270,7 +254,6 @@ function ChartItem(props: IChartItemProps) {
xAxis: dimensionX,
yAxis: dimensionY,
});
} else {
}
};
@@ -350,6 +333,13 @@ function ChartItem(props: IChartItemProps) {
};
}, [initDDL]);
const setBoundInfo = (boundInfo) => {
setChartData({
...chartData,
...boundInfo,
});
};
const renderEditorBlock = () => {
const { sqlData = {} } = chartData || {};
const options = Object.keys(sqlData).map((i) => ({ label: i, value: i }));
@@ -359,8 +349,9 @@ function ChartItem(props: IChartItemProps) {
<div className={styles.editorBlock}>
<div className={styles.editor}>
<ConsoleEditor
appendValue={initDDLMemo}
executeParams={chartData}
defaultValue={initDDLMemo.text}
boundInfo={chartData as any}
setBoundInfo={setBoundInfo}
hasAiChat={true}
hasAi2Lang={false}
hasSaveBtn={false}
@@ -368,14 +359,10 @@ function ChartItem(props: IChartItemProps) {
onExecuteSQL={(sql: string) => handleExecuteSQL(sql, chartData)}
editorOptions={{
lineNumbers: 'off',
theme:
appTheme.backgroundColor === ThemeType.Light
? EditorThemeType.DashboardLightTheme
: EditorThemeType.DashboardBlackTheme,
}}
isActive={true}
/>
<Cascader
{/* <Cascader
options={cascaderOption}
value={cascaderValue}
loadData={loadData}
@@ -384,7 +371,7 @@ function ChartItem(props: IChartItemProps) {
dataSourceId: '',
};
//包含了dataSourceId、databaseName、schemaName
(selectedOptions || []).forEach((o) => {
(selectedOptions || []).forEach((o: any) => {
if (o.type) {
p[`${o.type}Name`] = o.value;
} else {
@@ -400,7 +387,7 @@ function ChartItem(props: IChartItemProps) {
className={styles.dataSourceSelect}
placeholder={i18n('dashboard.editor.cascader.placeholder')}
// style={{ width: '100%' }}
/>
/> */}
</div>
<div className={styles.chartParamsForm}>
<div className={styles.chartParamsFormTitle}>Charts:</div>

View File

@@ -1,9 +1,7 @@
import React, { ForwardedRef, LegacyRef, useImperativeHandle, useMemo, useRef } from 'react';
import React, { ForwardedRef, forwardRef, useImperativeHandle, useMemo, useRef } from 'react';
import * as charts from 'echarts';
import ReactEcharts from 'echarts-for-react';
import './index.less';
import { Button } from 'antd';
import { forwardRef } from 'react';
type EChartsOption = charts.EChartsOption;
interface IProps {
@@ -24,7 +22,6 @@ const PieChart = (props: IProps, ref: ForwardedRef<{ getEchartsInstance: Functio
type: 'scroll', //分页类型
},
series: [
{
type: 'pie',
@@ -38,12 +35,11 @@ const PieChart = (props: IProps, ref: ForwardedRef<{ getEchartsInstance: Functio
label: {
show: true,
fontSize: 16,
fontWeight: 'bold'
}
fontWeight: 'bold',
},
},
},
],
}),
[props.data],
);

View File

@@ -1,8 +1,7 @@
import React, { useEffect, useState, useRef } from 'react';
import { Dropdown, Form, Input, Modal, message } from 'antd';
import { connect, Dispatch } from 'umi';
import cs from 'classnames';
import { IChartItem, IConnectionDetails, IDashboardItem, ITreeNode } from '@/typings';
import { IChartItem, IDashboardItem } from '@/typings';
import DraggableContainer from '@/components/DraggableContainer';
import Iconfont from '@/components/Iconfont';
import ChartItem from './chart-item';
@@ -16,18 +15,15 @@ import {
updateDashboard,
} from '@/service/dashboard';
import i18n from '@/i18n';
import { IConnectionModelState } from '@/models/connection';
import styles from './index.less';
import { IWorkspaceModelState } from '@/models/workspace';
import { IAIState } from '@/models/ai';
import { IRemainingUse } from '@/typings/ai';
// import { IConnectionModelState } from '@/models/connection';
// import { IWorkspaceModelState } from '@/models/workspace';
// import { IAIState } from '@/models/ai';
import { useConnectionStore } from '../store/connection';
import { useSettingStore } from '@/store/setting';
interface IProps {
className?: string;
connectionList: IConnectionDetails[];
curTableList: ITreeNode[];
remainingUse: IRemainingUse;
dispatch: Dispatch;
}
export const initChartItem: IChartItem = {
@@ -38,23 +34,20 @@ export const initChartItem: IChartItem = {
};
function Chart(props: IProps) {
const { className, connectionList, curTableList, remainingUse, dispatch } = props;
const { className } = props;
const [dashboardList, setDashboardList] = useState<IDashboardItem[]>([]);
const [curDashboard, setCurDashboard] = useState<IDashboardItem>();
const [openAddDashboard, setOpenAddDashboard] = useState(false);
const connectionList = useConnectionStore((state) => state.connectionList);
const remainingUse = useSettingStore((state) => state.remainingUse);
const [form] = Form.useForm(); // 创建一个表单实例
const [messageApi, contextHolder] = message.useMessage();
const [messageApi] = message.useMessage();
const draggableRef = useRef<any>();
useEffect(() => {
// 获取列表数据
queryDashboardList();
// 如果没有连接池,触发一次请求
dispatch({
type: 'connection/fetchConnectionList',
});
}, []);
useEffect(() => {
@@ -68,19 +61,19 @@ function Chart(props: IProps) {
}, [curDashboard]);
const queryDashboardList = async () => {
let res = await getDashboardList({});
const res = await getDashboardList({});
const { data } = res;
if (Array.isArray(data) && data.length > 0) {
setDashboardList(data);
let curDashboard = await getDashboardById({ id: data[0].id });
setCurDashboard(curDashboard);
const _curDashboard = await getDashboardById({ id: data[0].id });
setCurDashboard(_curDashboard);
}
};
const initCreateChart = async (dashboard?: IDashboardItem) => {
if (!dashboard) return;
let chartId = await createChart({});
const chartId = await createChart({});
const newDashboard = {
...dashboard,
schema: JSON.stringify([[chartId]]),
@@ -95,7 +88,7 @@ function Chart(props: IProps) {
if (curDashboard?.id === id) {
return;
}
let res = await getDashboardById({ id });
const res = await getDashboardById({ id });
setCurDashboard(res);
};
@@ -153,7 +146,7 @@ function Chart(props: IProps) {
const { id, schema, chartIds = [] } = curDashboard || {};
const chartList: number[][] = JSON.parse(schema || '') || [[]];
let chartId = await createChart({});
const chartId = await createChart({});
switch (type) {
case 'top':
chartList.splice(rowIndex, 0, [chartId]);
@@ -195,7 +188,7 @@ function Chart(props: IProps) {
id: id!,
...curDashboard,
schema: JSON.stringify(chartList),
chartIds: chartIds?.filter((id) => id !== chartId),
chartIds: chartIds?.filter((_id) => _id !== chartId),
};
await updateDashboard(newDashboard);
setCurDashboard(newDashboard);
@@ -218,7 +211,11 @@ function Chart(props: IProps) {
{chartList.map((rowData: number[], rowIndex: number) => (
<div key={rowIndex} className={styles.boxRightContentRow}>
{rowData.map((chartId: number, colIndex: number) => (
<div className={styles.boxRightContentColumn} style={{ width: `${100 / rowData.length}%` }}>
<div
key={colIndex}
className={styles.boxRightContentColumn}
style={{ width: `${100 / rowData.length}%` }}
>
<ChartItem
id={chartId}
key={chartId}
@@ -229,7 +226,6 @@ function Chart(props: IProps) {
addChartRight={() => onAddChart('right', rowIndex, colIndex)}
onDelete={(id: number) => onDeleteChart(id, rowIndex, colIndex)}
connectionList={connectionList || []}
tableList={curTableList || []}
remainingUse={remainingUse}
/>
</div>
@@ -261,7 +257,7 @@ function Chart(props: IProps) {
open={openAddDashboard}
onOk={async () => {
try {
const values = await form.validateFields();
await form.validateFields();
const formValue = form.getFieldsValue(true);
const { id } = formValue;
@@ -301,18 +297,4 @@ function Chart(props: IProps) {
);
}
export default connect(
({
connection,
workspace,
ai,
}: {
connection: IConnectionModelState;
workspace: IWorkspaceModelState;
ai: IAIState;
}) => ({
connectionList: connection.connectionList,
curTableList: workspace.curTableList,
remainingUse: ai.remainingUse,
}),
)(Chart);
export default Chart;

View File

@@ -29,6 +29,10 @@ export interface ITreeNode {
loadData?: (params:{refresh: boolean}) => void; // 加载数据的方法
// 父元素
parentNode?: ITreeNode;
// 分页
page?: number;
pageSize?: number;
total?: number;
}
// 视图 函数 触发器 过程 通用的返回结果