diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx
index 66386f7d7..0bf1f3ab4 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx
@@ -1,7 +1,7 @@
import { Tabs, Popover, message } from 'antd';
import React, { useEffect, useState } from 'react';
import { connect, Helmet, useParams, history } from 'umi';
-import ProjectListTree from './components/ProjectList';
+import DomainListTree from './components/DomainList';
import styles from './components/style.less';
import type { StateType } from './model';
import { DownOutlined } from '@ant-design/icons';
@@ -20,7 +20,6 @@ type Props = {
};
const ChatSetting: React.FC
= ({ domainManger, dispatch }) => {
- window.RUNNING_ENV = 'chat';
const defaultTabKey = 'metric';
const params: any = useParams();
const menuKey = params.menuKey ? params.menuKey : defaultTabKey;
@@ -159,7 +158,7 @@ const ChatSetting: React.FC = ({ domainManger, dispatch }) => {
maxHeight: '800px',
}}
content={
- {
setOpen(false);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx
index 42ec92ef4..72334174f 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx
@@ -467,21 +467,16 @@ const SqlDetail: React.FC = ({
-
-
-
+ // theme="monokai"
+ isRightTheme={isRight}
+ sizeChanged={editorSize}
+ onSqlChange={onSqlChange}
+ onSelect={onSelect}
+ />
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx
index 02328c9d3..ee6f77d13 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx
@@ -3,9 +3,11 @@ import StandardFormRow from '@/components/StandardFormRow';
import TagSelect from '@/components/TagSelect';
import React, { useEffect } from 'react';
import { SENSITIVE_LEVEL_OPTIONS } from '../../constant';
+import { SearchOutlined } from '@ant-design/icons';
+import DomainTreeSelect from '../../components/DomainTreeSelect';
+import styles from '../style.less';
const FormItem = Form.Item;
-const { Option } = Select;
type Props = {
filterValues?: any;
@@ -25,10 +27,7 @@ const MetricFilter: React.FC = ({ filterValues = {}, onFiltersChange }) =
onFiltersChange(value, values);
};
- const onSearch = (value) => {
- if (!value) {
- return;
- }
+ const onSearch = (value: any) => {
onFiltersChange(value, form.getFieldsValue());
};
@@ -57,34 +56,24 @@ const MetricFilter: React.FC = ({ filterValues = {}, onFiltersChange }) =
form={form}
colon={false}
onValuesChange={(value, values) => {
- if (value.keywords || value.keywordsType) {
+ if (value.name) {
return;
}
handleValuesChange(value, values);
}}
- initialValues={{
- keywordsType: 'name',
- }}
>
-
-
-
+
+
+
+ }
+ onSearch={onSearch}
+ />
+
-
-
-
-
+
{filterList.map((item) => {
const { title, key, options } = item;
@@ -102,6 +91,11 @@ const MetricFilter: React.FC = ({ filterValues = {}, onFiltersChange }) =
);
})}
+
+
+
+
+
);
};
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/index.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/index.tsx
index a04839196..da305e98e 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/index.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/index.tsx
@@ -1,6 +1,6 @@
import type { ActionType, ProColumns } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
-import { message, Space } from 'antd';
+import { message } from 'antd';
import React, { useRef, useState, useEffect } from 'react';
import type { Dispatch } from 'umi';
import { connect } from 'umi';
@@ -23,6 +23,7 @@ type QueryMetricListParams = {
bizName?: string;
sensitiveLevel?: string;
type?: string;
+ [key: string]: any;
};
const ClassMetricTable: React.FC = () => {
@@ -31,8 +32,9 @@ const ClassMetricTable: React.FC = () => {
pageSize: 20,
total: 0,
});
-
+ const [loading, setLoading] = useState(false);
const [dataSource, setDataSource] = useState([]);
+ const [filterParams, setFilterParams] = useState>({});
const actionRef = useRef();
useEffect(() => {
@@ -40,11 +42,13 @@ const ClassMetricTable: React.FC = () => {
}, []);
const queryMetricList = async (params: QueryMetricListParams = {}) => {
+ setLoading(true);
const { code, data, msg } = await queryMetric({
- ...params,
...pagination,
+ ...params,
});
- const { list, pageSize, current, total } = data;
+ setLoading(false);
+ const { list, pageSize, current, total } = data || {};
let resData: any = {};
if (code === 200) {
setPagination({
@@ -87,6 +91,10 @@ const ClassMetricTable: React.FC = () => {
dataIndex: 'bizName',
title: '字段名称',
},
+ {
+ dataIndex: 'domainName',
+ title: '主题域',
+ },
{
dataIndex: 'sensitiveLevel',
title: '敏感度',
@@ -105,7 +113,6 @@ const ClassMetricTable: React.FC = () => {
{
dataIndex: 'type',
title: '指标类型',
- // search: false,
valueEnum: {
ATOMIC: '原子指标',
DERIVED: '衍生指标',
@@ -123,25 +130,18 @@ const ClassMetricTable: React.FC = () => {
];
const handleFilterChange = async (filterParams: {
- keywordsType: string;
- keywords: string;
+ name: string;
sensitiveLevel: string;
type: string;
}) => {
- const params: QueryMetricListParams = {};
- const { keywordsType, keywords, sensitiveLevel, type } = filterParams;
- if (keywordsType && keywords) {
- params[keywordsType] = keywords;
- }
+ const { sensitiveLevel, type } = filterParams;
+ const params: QueryMetricListParams = { ...filterParams };
const sensitiveLevelValue = sensitiveLevel?.[0];
const typeValue = type?.[0];
- if (sensitiveLevelValue) {
- params.sensitiveLevel = sensitiveLevelValue;
- }
- if (type) {
- params.type = typeValue;
- }
+ params.sensitiveLevel = sensitiveLevelValue;
+ params.type = typeValue;
+ setFilterParams(params);
await queryMetricList(params);
};
@@ -157,7 +157,6 @@ const ClassMetricTable: React.FC = () => {
= () => {
tableAlertRender={() => {
return false;
}}
+ loading={loading}
onChange={(data: any) => {
const { current, pageSize, total } = data;
- setPagination({
+ const pagin = {
current,
pageSize,
total,
- });
+ };
+ setPagination(pagin);
+ queryMetricList({ ...pagin, ...filterParams });
}}
size="small"
options={{ reload: false, density: false, fullScreen: false }}
- // toolBarRender={() => [
- // ,
- // ]}
/>
>
);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/style.less b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/style.less
index 363e8c2ae..df680eae5 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/style.less
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/style.less
@@ -7,4 +7,77 @@
.metricTable {
margin: 20px;
+}
+
+
+
+.searchBox {
+ // margin-bottom: 12px;
+ background: #fff;
+ border-radius: 10px;
+ width: 500px;
+ margin: 0 auto;
+ .searchInput {
+ width: 100%;
+ border: 1px solid rgba(35, 104, 184, 0.6);
+ border-radius: 10px;
+ }
+ :global {
+
+ .ant-select-auto-complete {
+ width: 100%;
+ }
+ .ant-input {
+ height: 50px;
+ padding: 0 15px;
+ color: #515a6e;
+ font-size: 14px;
+ line-height: 50px;
+ background: hsla(0, 0%, 100%, 0.2);
+ border: none;
+ border-top-left-radius: 10px;
+ border-bottom-left-radius: 10px;
+ caret-color: #296df3;
+
+ &:focus {
+ border-right-width: 0 !important;
+ box-shadow: none;
+ }
+ }
+
+ .ant-input-group-addon {
+ left: 0 !important;
+ padding: 0;
+ background: hsla(0, 0%, 100%, 0.2);
+ border: none;
+ border-top-left-radius: 0;
+ border-top-right-radius: 10px;
+ border-bottom-right-radius: 10px;
+ border-bottom-left-radius: 0;
+
+ .ant-btn {
+ width: 72px;
+ height: 50px;
+ margin: 0;
+ color: rgba(35, 104, 184, 0.6);
+ font-size: 16px;
+ background-color: transparent;
+ background-color: transparent;
+ border: none;
+ box-shadow: none !important;
+
+ &::after {
+ box-shadow: none !important;
+ }
+
+ .anticon {
+ font-size: 28px;
+
+ &:hover {
+ color: @primary-color;
+ }
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx
index 69bb7a12a..8feacfc92 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx
@@ -1,7 +1,7 @@
import { Tabs, Popover, message } from 'antd';
import React, { useEffect, useState } from 'react';
import { connect, Helmet, history, useParams } from 'umi';
-import ProjectListTree from './components/ProjectList';
+import DomainListTree from './components/DomainList';
import ClassDataSourceTable from './components/ClassDataSourceTable';
import ClassDimensionTable from './components/ClassDimensionTable';
import ClassMetricTable from './components/ClassMetricTable';
@@ -24,7 +24,6 @@ type Props = {
};
const DomainManger: React.FC = ({ domainManger, dispatch }) => {
- window.RUNNING_ENV = 'semantic';
const defaultTabKey = 'xflow';
const params: any = useParams();
const menuKey = params.menuKey ? params.menuKey : defaultTabKey;
@@ -203,7 +202,7 @@ const DomainManger: React.FC = ({ domainManger, dispatch }) => {
maxHeight: '800px',
}}
content={
- {
setOpen(false);
}}
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ContextMenu.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ContextMenu.tsx
index 3619f129d..d4e46855b 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ContextMenu.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ContextMenu.tsx
@@ -6,7 +6,6 @@ import { SemanticNodeType } from '../../enum';
import { SEMANTIC_NODE_TYPE_CONFIG } from '../../constant';
type InitContextMenuProps = {
- graphShowType?: string;
onMenuClick?: (key: string, item: Item) => void;
};
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/GraphLegend.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/GraphLegend.tsx
new file mode 100644
index 000000000..3edfd5844
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/GraphLegend.tsx
@@ -0,0 +1,83 @@
+import { Space, Checkbox } from 'antd';
+import type { CheckboxValueType } from 'antd/es/checkbox/Group';
+import React, { useState, useEffect } from 'react';
+
+import { connect } from 'umi';
+import type { StateType } from '../../model';
+import styles from '../style.less';
+
+type Props = {
+ legendOptions: LegendOptionsItem[];
+ value?: string[];
+ domainManger: StateType;
+ onChange?: (ids: CheckboxValueType[]) => void;
+ defaultCheckAll?: boolean;
+ [key: string]: any;
+};
+
+type LegendOptionsItem = {
+ id: string;
+ label: string;
+};
+
+const GraphLegend: React.FC = ({
+ legendOptions,
+ value,
+ defaultCheckAll = false,
+ onChange,
+}) => {
+ const [groupValue, setGroupValue] = useState(value || []);
+
+ useEffect(() => {
+ if (!defaultCheckAll) {
+ return;
+ }
+ if (!Array.isArray(legendOptions)) {
+ setGroupValue([]);
+ return;
+ }
+ setGroupValue(
+ legendOptions.map((item) => {
+ return item.id;
+ }),
+ );
+ }, [legendOptions]);
+
+ useEffect(() => {
+ if (!Array.isArray(value)) {
+ setGroupValue([]);
+ return;
+ }
+ setGroupValue(value);
+ }, [value]);
+
+ const handleChange = (checkedValues: CheckboxValueType[]) => {
+ setGroupValue(checkedValues);
+ onChange?.(checkedValues);
+ };
+
+ return (
+
+
+
+
可见数据源
+
+
+ {legendOptions.map((item) => {
+ return (
+
+ {item.label}
+
+ );
+ })}
+
+
+
+
+
+ );
+};
+
+export default connect(({ domainManger }: { domainManger: StateType }) => ({
+ domainManger,
+}))(GraphLegend);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/GraphLegendVisibleModeItem.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/GraphLegendVisibleModeItem.tsx
new file mode 100644
index 000000000..103147b7f
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/GraphLegendVisibleModeItem.tsx
@@ -0,0 +1,42 @@
+import { Segmented } from 'antd';
+
+import React from 'react';
+import { SemanticNodeType } from '../../enum';
+import styles from '../style.less';
+
+type Props = {
+ value?: SemanticNodeType;
+ onChange?: (value: SemanticNodeType) => void;
+ [key: string]: any;
+};
+
+const GraphLegendVisibleModeItem: React.FC = ({ value, onChange }) => {
+ return (
+
+ {
+ onChange?.(changeValue as SemanticNodeType);
+ }}
+ options={[
+ {
+ value: '',
+ label: '全部',
+ },
+ {
+ value: SemanticNodeType.DIMENSION,
+ label: '仅维度',
+ },
+ {
+ value: SemanticNodeType.METRIC,
+ label: '仅指标',
+ },
+ ]}
+ />
+
+ );
+};
+
+export default GraphLegendVisibleModeItem;
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/NodeInfoDrawer.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/NodeInfoDrawer.tsx
index fa5d78e1d..46850eeff 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/NodeInfoDrawer.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/NodeInfoDrawer.tsx
@@ -95,6 +95,7 @@ const NodeInfoDrawer: React.FC = ({
},
{
label: '别名',
+ hideItem: !alias,
value: alias || '-',
},
{
@@ -213,7 +214,34 @@ const NodeInfoDrawer: React.FC = ({
message.error(msg);
}
};
+ const extraNode = (
+
+
+
+ {
+ handleDeleteConfirm();
+ }}
+ >
+
+
+
+
+ );
return (
<>
= ({
placement="right"
mask={false}
getContainer={false}
- footer={
-
-
-
-
- {
- handleDeleteConfirm();
- }}
- >
-
-
-
-
- }
+ footer={false}
{...restProps}
>
@@ -282,6 +283,7 @@ const NodeInfoDrawer: React.FC
= ({
);
})}
+ {extraNode}
>
);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolBar.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolBar.tsx
index 44d6bc508..abcae06f0 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolBar.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolBar.tsx
@@ -1,8 +1,9 @@
import G6, { Graph } from '@antv/g6';
+import { IAbstractGraph as IGraph } from '@antv/g6-core';
import { createDom } from '@antv/dom-util';
import { ToolBarSearchCallBack } from '../../data';
const searchIconSvgPath = ``;
-
+const visibleModeIconSvgPath = ``;
// const searchNode = (graph) => {
// const toolBarSearchInput = document.getElementById('toolBarSearchInput') as HTMLInputElement;
// const searchText = toolBarSearchInput.value.trim();
@@ -90,13 +91,29 @@ const searchInputDOM = (graph: Graph, onSearch: ToolBarSearchCallBack) => {
return searchDom;
};
-const initToolBar = ({ onSearch }: { onSearch: ToolBarSearchCallBack }) => {
+function zoomGraph(graph, ratio) {
+ const width = graph.get('width');
+ const height = graph.get('height');
+ const centerX = width / 2;
+ const centerY = height / 2;
+ graph.zoom(ratio, { x: centerX, y: centerY });
+}
+
+const initToolBar = ({
+ onSearch,
+ onClick,
+}: {
+ onSearch: ToolBarSearchCallBack;
+ onClick?: (code: string, graph: IGraph) => void;
+}) => {
const toolBarInstance = new G6.ToolBar();
const config = toolBarInstance._cfgs;
const defaultContentDomString = config.getContent();
const defaultContentDom = createDom(defaultContentDomString);
// @ts-ignore
- const elements = defaultContentDom.querySelectorAll('li[code="redo"], li[code="undo"]');
+ const elements = defaultContentDom.querySelectorAll(
+ 'li[code="redo"], li[code="undo"], li[code="realZoom"]',
+ );
elements.forEach((element) => {
element.remove();
});
@@ -113,7 +130,21 @@ const initToolBar = ({ onSearch }: { onSearch: ToolBarSearchCallBack }) => {
${searchIconSvgPath}
`;
- defaultContentDom.insertAdjacentHTML('afterbegin', searchBtnDom);
+ const visibleBtnDom = `
+
+ `;
+
+ defaultContentDom.insertAdjacentHTML('afterbegin', `${searchBtnDom}${visibleBtnDom}`);
let searchInputContentVisible = true;
const toolbar = new G6.ToolBar({
position: { x: 20, y: 20 },
@@ -122,6 +153,16 @@ const initToolBar = ({ onSearch }: { onSearch: ToolBarSearchCallBack }) => {
const searchInput = searchInputDOM(graph as Graph, onSearch);
const content = `${defaultContentDom.outerHTML}
`;
const contentDom = createDom(content);
+ contentDom.addEventListener('click', (event: PointerEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+ return false;
+ });
+ contentDom.addEventListener('dblclick', (event: PointerEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+ return false;
+ });
contentDom.appendChild(searchInput);
return contentDom;
},
@@ -133,11 +174,54 @@ const initToolBar = ({ onSearch }: { onSearch: ToolBarSearchCallBack }) => {
searchText.style.display = visible;
searchInputContentVisible = !searchInputContentVisible;
}
+ } else if (code === 'visibleMode') {
+ const searchText = document.getElementById('searchInputContent');
+ if (searchText) {
+ const visible = 'none';
+ searchText.style.display = visible;
+ searchInputContentVisible = false;
+ }
+ } else if (code.includes('zoom')) {
+ const sensitivity = 0.1; // 设置缩放灵敏度,值越小,缩放越不敏感,默认值为 1
+ const zoomInRatio = 1 - sensitivity;
+ const zoomOutRatio = 1 + sensitivity;
+
+ if (code === 'zoomIn') {
+ zoomGraph(graph, zoomInRatio);
+ } else if (code === 'zoomOut') {
+ zoomGraph(graph, zoomOutRatio);
+ }
+ // else if (code === 'realZoom') {
+ // const width = graph.get('width');
+ // const height = graph.get('height');
+ // const centerX = width / 2;
+ // const centerY = height / 2;
+ // graph.moveTo(centerX, centerY);
+ // graph.zoomTo(1, { x: centerX, y: centerY });
+ // } else if (code === 'autoZoom') {
+ // const width = graph.get('width');
+ // const height = graph.get('height');
+ // const centerX = width / 2;
+ // const centerY = height / 2;
+ // const centerModel = graph.getPointByCanvas(centerX, centerY);
+
+ // // 调用 fitView
+ // graph.fitView();
+
+ // // 在 fitView 之后获取新的画布中心点
+ // const newCenterCanvas = graph.getCanvasByPoint(centerModel.x, centerModel.y);
+
+ // // 计算并调整画布的偏移量,使得画布中心点保持不变
+ // const dx = centerX - newCenterCanvas.x;
+ // const dy = centerY - newCenterCanvas.y;
+ // graph.translate(dx, dy);
+ // }
} else {
// handleDefaultOperator public方法缺失graph作为参数传入,将graph挂载在cfgs上,源码通过get会获取到graph,完成默认code的执行逻辑
toolBarInstance._cfgs.graph = graph;
toolBarInstance.handleDefaultOperator(code);
}
+ onClick?.(code, graph);
},
});
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx
index cd4176cdc..49911df5f 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx
@@ -1,7 +1,7 @@
import React, { useEffect, useState, useRef } from 'react';
import { connect } from 'umi';
import type { StateType } from '../model';
-import { IGroup } from '@antv/g-base';
+// import { IGroup } from '@antv/g-base';
import type { Dispatch } from 'umi';
import {
typeConfigs,
@@ -16,7 +16,7 @@ import { Item, TreeGraphData, NodeConfig, IItemBaseConfig } from '@antv/g6-core'
import initToolBar from './components/ToolBar';
import initTooltips from './components/ToolTips';
import initContextMenu from './components/ContextMenu';
-import initLegend from './components/Legend';
+// import initLegend from './components/Legend';
import { SemanticNodeType } from '../enum';
import G6 from '@antv/g6';
import { ISemantic, IDataSource } from '../data';
@@ -26,11 +26,14 @@ import MetricInfoCreateForm from '../components/MetricInfoCreateForm';
import DeleteConfirmModal from './components/DeleteConfirmModal';
import ClassDataSourceTypeModal from '../components/ClassDataSourceTypeModal';
import GraphToolBar from './components/GraphToolBar';
-import { cloneDeep } from 'lodash';
+import GraphLegend from './components/GraphLegend';
+import GraphLegendVisibleModeItem from './components/GraphLegendVisibleModeItem';
+
+// import { cloneDeep } from 'lodash';
type Props = {
domainId: number;
- graphShowType?: SemanticNodeType;
+ // graphShowType?: SemanticNodeType;
domainManger: StateType;
dispatch: Dispatch;
};
@@ -39,7 +42,7 @@ const DomainManger: React.FC = ({
domainManger,
domainId,
// graphShowType = SemanticNodeType.DIMENSION,
- graphShowType,
+ // graphShowType,
dispatch,
}) => {
const ref = useRef(null);
@@ -53,7 +56,7 @@ const DomainManger: React.FC = ({
const legendDataRef = useRef([]);
const graphRef = useRef(null);
- const legendDataFilterFunctions = useRef({});
+ // const legendDataFilterFunctions = useRef({});
const [dimensionItem, setDimensionItem] = useState();
const [metricItem, setMetricItem] = useState();
@@ -70,30 +73,19 @@ const DomainManger: React.FC = ({
const [confirmModalOpenState, setConfirmModalOpenState] = useState(false);
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
- // const toggleNodeVisibility = (graph: Graph, node: Item, visible: boolean) => {
- // if (visible) {
- // graph.showItem(node);
- // } else {
- // graph.hideItem(node);
- // }
- // };
+ const visibleModeOpenRef = useRef(false);
+ const [visibleModeOpen, setVisibleModeOpen] = useState(false);
+
+ const graphShowTypeRef = useRef();
+ const [graphShowTypeState, setGraphShowTypeState] = useState();
+
+ const graphLegendDataSourceIds = useRef();
useEffect(() => {
dimensionListRef.current = dimensionList;
metricListRef.current = metricList;
}, [dimensionList, metricList]);
- // const toggleChildrenVisibility = (graph: Graph, node: Item, visible: boolean) => {
- // const model = node.getModel();
- // if (Array.isArray(model.children)) {
- // model.children.forEach((child) => {
- // const childNode = graph.findById(child.id);
- // toggleNodeVisibility(graph, childNode, visible);
- // toggleChildrenVisibility(graph, childNode, visible);
- // });
- // }
- // };
-
const handleSeachNode = (text: string) => {
const filterData = dataSourceRef.current.reduce(
(data: ISemantic.IDomainSchemaRelaList, item: ISemantic.IDomainSchemaRelaItem) => {
@@ -117,12 +109,24 @@ const DomainManger: React.FC = ({
refreshGraphData(rootGraphData);
};
- const changeGraphData = (
- dataSourceList: ISemantic.IDomainSchemaRelaList,
- type?: SemanticNodeType,
- ): TreeGraphData => {
- const relationData = formatterRelationData({ dataSourceList, type, limit: 20 });
- const legendList = relationData.map((item: any) => {
+ const changeGraphData = (dataSourceList: ISemantic.IDomainSchemaRelaList): TreeGraphData => {
+ const relationData = formatterRelationData({
+ dataSourceList,
+ type: graphShowTypeRef.current,
+ limit: 20,
+ showDataSourceId: graphLegendDataSourceIds.current,
+ });
+
+ const graphRootData = {
+ id: 'root',
+ name: domainManger.selectDomainName,
+ children: relationData,
+ };
+ return graphRootData;
+ };
+
+ const initLegendData = (graphRootData: TreeGraphData) => {
+ const legendList = graphRootData?.children?.map((item: any) => {
const { id, name } = item;
return {
id,
@@ -131,13 +135,7 @@ const DomainManger: React.FC = ({
...typeConfigs.datasource,
};
});
- legendDataRef.current = legendList;
- const graphRootData = {
- id: 'root',
- name: domainManger.selectDomainName,
- children: relationData,
- };
- return graphRootData;
+ legendDataRef.current = legendList as any;
};
const queryDataSourceList = async (params: {
@@ -153,8 +151,9 @@ const DomainManger: React.FC = ({
}),
);
const graphRootData = changeGraphData(data);
- setGraphData(graphRootData);
dataSourceRef.current = data;
+ initLegendData(graphRootData);
+ setGraphData(graphRootData);
return graphRootData;
}
return false;
@@ -164,41 +163,42 @@ const DomainManger: React.FC = ({
};
useEffect(() => {
+ graphLegendDataSourceIds.current = undefined;
graphRef.current = null;
queryDataSourceList({ domainId });
- }, [domainId, graphShowType]);
+ }, [domainId]);
- const getLegendDataFilterFunctions = () => {
- legendDataRef.current.map((item: any) => {
- const { id } = item;
- legendDataFilterFunctions.current = {
- ...legendDataFilterFunctions.current,
- [id]: (d: any) => {
- if (d.legendType === id) {
- return true;
- }
- return false;
- },
- };
- });
- };
+ // const getLegendDataFilterFunctions = () => {
+ // legendDataRef.current.map((item: any) => {
+ // const { id } = item;
+ // legendDataFilterFunctions.current = {
+ // ...legendDataFilterFunctions.current,
+ // [id]: (d: any) => {
+ // if (d.legendType === id) {
+ // return true;
+ // }
+ // return false;
+ // },
+ // };
+ // });
+ // };
- const setAllActiveLegend = (legend: any) => {
- const legendCanvas = legend._cfgs.legendCanvas;
- if (!legendCanvas) {
- return;
- }
- // 从图例中找出node-group节点;
- const group = legendCanvas.find((e: any) => e.get('name') === 'node-group');
- // 数据源的图例节点在node-group中的children中;
- const groups = group.get('children');
- groups.forEach((itemGroup: any) => {
- const labelText = itemGroup.find((e: any) => e.get('name') === 'circle-node-text');
- // legend中activateLegend事件触发在图例节点的Text上,方法中存在向上溯源的逻辑:const shapeGroup = shape.get('parent');
- // 因此复用实例方法时,在这里不能直接将图例节点传入,需要在节点的children中找任意一个元素作为入参;
- legend.activateLegend(labelText);
- });
- };
+ // const setAllActiveLegend = (legend: any) => {
+ // const legendCanvas = legend._cfgs.legendCanvas;
+ // if (!legendCanvas) {
+ // return;
+ // }
+ // // 从图例中找出node-group节点;
+ // const group = legendCanvas.find((e: any) => e.get('name') === 'node-group');
+ // // 数据源的图例节点在node-group中的children中;
+ // const groups = group.get('children');
+ // groups.forEach((itemGroup: any) => {
+ // const labelText = itemGroup.find((e: any) => e.get('name') === 'circle-node-text');
+ // // legend中activateLegend事件触发在图例节点的Text上,方法中存在向上溯源的逻辑:const shapeGroup = shape.get('parent');
+ // // 因此复用实例方法时,在这里不能直接将图例节点传入,需要在节点的children中找任意一个元素作为入参;
+ // legend.activateLegend(labelText);
+ // });
+ // };
const handleContextMenuClickEdit = (item: IItemBaseConfig) => {
const targetData = item.model;
@@ -273,7 +273,6 @@ const DomainManger: React.FC = ({
if (targetData.nodeType === SemanticNodeType.DIMENSION) {
const targetItem = dimensionListRef.current.find((item) => item.id === targetData.uid);
if (targetItem) {
- // setDimensionItem({ ...targetItem });
setCurrentNodeData(targetItem);
setConfirmModalOpenState(true);
} else {
@@ -283,7 +282,6 @@ const DomainManger: React.FC = ({
if (targetData.nodeType === SemanticNodeType.METRIC) {
const targetItem = metricListRef.current.find((item) => item.id === targetData.uid);
if (targetItem) {
- // setMetricItem({ ...targetItem });
setCurrentNodeData(targetItem);
setConfirmModalOpenState(true);
} else {
@@ -357,6 +355,16 @@ const DomainManger: React.FC = ({
},
};
+ function handleToolBarClick(code: string) {
+ if (code === 'visibleMode') {
+ visibleModeOpenRef.current = !visibleModeOpenRef.current;
+ setVisibleModeOpen(visibleModeOpenRef.current);
+ return;
+ }
+ visibleModeOpenRef.current = false;
+ setVisibleModeOpen(false);
+ }
+
useEffect(() => {
if (!Array.isArray(graphData?.children)) {
return;
@@ -371,17 +379,16 @@ const DomainManger: React.FC = ({
const graphNodeList = flatGraphDataNode(graphData.children);
const graphConfigKey = graphNodeList.length > 20 ? 'dendrogram' : 'mindmap';
- getLegendDataFilterFunctions();
- const toolbar = initToolBar({ onSearch: handleSeachNode });
+ // getLegendDataFilterFunctions();
+ const toolbar = initToolBar({ onSearch: handleSeachNode, onClick: handleToolBarClick });
const tooltip = initTooltips();
const contextMenu = initContextMenu({
- graphShowType,
onMenuClick: handleContextMenuClick,
});
- const legend = initLegend({
- nodeData: legendDataRef.current,
- filterFunctions: { ...legendDataFilterFunctions.current },
- });
+ // const legend = initLegend({
+ // nodeData: legendDataRef.current,
+ // filterFunctions: { ...legendDataFilterFunctions.current },
+ // });
graphRef.current = new G6.TreeGraph({
container: 'semanticGraph',
@@ -400,7 +407,10 @@ const DomainManger: React.FC = ({
'drag-node',
'drag-canvas',
// 'activate-relations',
- 'zoom-canvas',
+ {
+ type: 'zoom-canvas',
+ sensitivity: 0.3, // 设置缩放灵敏度,值越小,缩放越不敏感,默认值为 1
+ },
{
type: 'activate-relations',
trigger: 'mouseenter', // 触发方式,可以是 'mouseenter' 或 'click'
@@ -429,45 +439,35 @@ const DomainManger: React.FC = ({
layout: {
...graphConfigMap[graphConfigKey].layout,
},
- plugins: [legend, tooltip, toolbar, contextMenu],
+ plugins: [tooltip, toolbar, contextMenu],
+ // plugins: [legend, tooltip, toolbar, contextMenu],
});
graphRef.current.set('initGraphData', graphData);
graphRef.current.set('initDataSource', dataSourceRef.current);
- const legendCanvas = legend._cfgs.legendCanvas;
+ // const legendCanvas = legend._cfgs.legendCanvas;
// legend模式事件方法bindEvents会有点击图例空白清空选中的逻辑,在注册click事件前,先将click事件队列清空;
- legend._cfgs.legendCanvas._events.click = [];
- // legendCanvas.on('click', (e) => {
- // const shape = e.target;
- // const shapeGroup = shape.get('parent');
- // const shapeGroupId = shapeGroup?.cfg?.id;
- // if (shapeGroupId) {
- // const isActive = shapeGroup.get('active');
- // const targetNode = graphRef.current.findById(shapeGroupId);
- // toggleNodeVisibility(graphRef.current, targetNode, isActive);
- // toggleChildrenVisibility(graphRef.current, targetNode, isActive);
- // }
+ // legend._cfgs.legendCanvas._events.click = [];
+ // legendCanvas.on('click', () => {
+ // // @ts-ignore findLegendItemsByState为Legend的 private方法,忽略ts校验
+ // const activedNodeList = legend.findLegendItemsByState('active');
+ // // 获取当前所有激活节点后进行数据遍历筛选;
+ // const activedNodeIds = activedNodeList.map((item: IGroup) => {
+ // return item.cfg.id;
+ // });
+ // const graphDataClone = cloneDeep(graphData);
+ // const filterGraphDataChildren = Array.isArray(graphDataClone?.children)
+ // ? graphDataClone.children.reduce((children: TreeGraphData[], item: TreeGraphData) => {
+ // if (activedNodeIds.includes(item.id)) {
+ // children.push(item);
+ // }
+ // return children;
+ // }, [])
+ // : [];
+ // graphDataClone.children = filterGraphDataChildren;
+ // refreshGraphData(graphDataClone);
// });
- legendCanvas.on('click', () => {
- // @ts-ignore findLegendItemsByState为Legend的 private方法,忽略ts校验
- const activedNodeList = legend.findLegendItemsByState('active');
- // 获取当前所有激活节点后进行数据遍历筛选;
- const activedNodeIds = activedNodeList.map((item: IGroup) => {
- return item.cfg.id;
- });
- const graphDataClone = cloneDeep(graphData);
- const filterGraphDataChildren = Array.isArray(graphDataClone?.children)
- ? graphDataClone.children.reduce((children: TreeGraphData[], item: TreeGraphData) => {
- if (activedNodeIds.includes(item.id)) {
- children.push(item);
- }
- return children;
- }, [])
- : [];
- graphDataClone.children = filterGraphDataChildren;
- refreshGraphData(graphDataClone);
- });
graphRef.current.node(function (node: NodeConfig) {
return getNodeConfigByType(node, {
@@ -478,7 +478,7 @@ const DomainManger: React.FC = ({
graphRef.current.render();
graphRef.current.fitView([80, 80]);
- setAllActiveLegend(legend);
+ // setAllActiveLegend(legend);
graphRef.current.on('node:click', (evt: any) => {
const item = evt.item; // 被操作的节点 item
@@ -513,8 +513,11 @@ const DomainManger: React.FC = ({
}
}, [graphData]);
- const updateGraphData = async () => {
- const graphRootData = await queryDataSourceList({ domainId });
+ const updateGraphData = async (params?: { graphShowType?: SemanticNodeType }) => {
+ const graphRootData = await queryDataSourceList({
+ domainId,
+ graphShowType: params?.graphShowType,
+ });
if (graphRootData) {
refreshGraphData(graphRootData);
}
@@ -529,6 +532,27 @@ const DomainManger: React.FC = ({
return (
<>
+ {
+ graphLegendDataSourceIds.current = nodeIds;
+ const rootGraphData = changeGraphData(dataSourceRef.current);
+ refreshGraphData(rootGraphData);
+ }}
+ />
+ {visibleModeOpen && (
+ {
+ graphShowTypeRef.current = showType;
+ setGraphShowTypeState(showType);
+ const rootGraphData = changeGraphData(dataSourceRef.current);
+ refreshGraphData(rootGraphData);
+ }}
+ />
+ )}
+
{
setNodeDataSource(undefined);
@@ -547,7 +571,7 @@ const DomainManger: React.FC = ({
/>
@@ -648,7 +672,7 @@ const DomainManger: React.FC = ({
onOkClick={() => {
setConfirmModalOpenState(false);
updateGraphData();
- graphShowType === SemanticNodeType.DIMENSION
+ graphShowTypeState === SemanticNodeType.DIMENSION
? dispatch({
type: 'domainManger/queryDimensionList',
payload: {
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/style.less b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/style.less
index 02383ba3b..7a780ddfe 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/style.less
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/style.less
@@ -19,4 +19,36 @@
border: 1px solid #0e73ff;
}
}
+}
+
+.graphLegend {
+ padding: 15px;
+ background: #45be940d;
+ position: absolute;
+ top: 100px;
+ left: 20px;
+ z-index: 1;
+ border: 1px solid #78c16d;
+ min-width: 190px;
+ .title {
+ text-align: center;
+ margin-bottom: 10px;
+ font-size: 10px;
+ font-weight: 500;
+ }
+ :global {
+ .ant-form-item {
+ margin-bottom: 2px;
+ }
+ }
+}
+.graphLegendVisibleModeItem {
+ padding: 3px;
+ background: #fff;
+ position: absolute;
+ top: 58px;
+ left: 20px;
+ z-index: 1;
+ border: 1px solid #eee;
+ min-width: 190px;
}
\ No newline at end of file
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/utils.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/utils.ts
index 1b77d9b10..bfa2742cc 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/utils.ts
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/utils.ts
@@ -67,8 +67,9 @@ export const formatterRelationData = (params: {
dataSourceList: ISemantic.IDomainSchemaRelaList;
limit?: number;
type?: SemanticNodeType;
+ showDataSourceId?: string[];
}): TreeGraphData[] => {
- const { type, dataSourceList, limit } = params;
+ const { type, dataSourceList, limit, showDataSourceId } = params;
const relationData = dataSourceList.reduce(
(relationList: TreeGraphData[], item: ISemantic.IDomainSchemaRelaItem) => {
const { datasource, dimensions, metrics } = item;
@@ -86,20 +87,22 @@ export const formatterRelationData = (params: {
const metricList = getMetricChildren(metrics, dataSourceNodeId, limit);
childrenList = [...dimensionList, ...metricList];
}
- relationList.push({
- ...datasource,
- legendType: dataSourceNodeId,
- id: dataSourceNodeId,
- uid: id,
- nodeType: SemanticNodeType.DATASOURCE,
- size: 40,
- children: [...childrenList],
- style: {
- lineWidth: 2,
- fill: '#BDEFDB',
- stroke: '#5AD8A6',
- },
- });
+ if (!showDataSourceId || showDataSourceId.includes(dataSourceNodeId)) {
+ relationList.push({
+ ...datasource,
+ legendType: dataSourceNodeId,
+ id: dataSourceNodeId,
+ uid: id,
+ nodeType: SemanticNodeType.DATASOURCE,
+ size: 40,
+ children: [...childrenList],
+ style: {
+ lineWidth: 2,
+ fill: '#BDEFDB',
+ stroke: '#5AD8A6',
+ },
+ });
+ }
return relationList;
},
[],
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx
index 40a7a73e9..4fc864a0a 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx
@@ -1,10 +1,10 @@
-import { Radio } from 'antd';
+// import { Radio } from 'antd';
import React, { useState } from 'react';
import { connect } from 'umi';
import styles from './components/style.less';
import type { StateType } from './model';
import { SemanticNodeType } from './enum';
-import SemanticFlow from './SemanticFlows';
+// import SemanticFlow from './SemanticFlows';
import SemanticGraph from './SemanticGraph';
type Props = {
@@ -12,7 +12,7 @@ type Props = {
};
const SemanticGraphCanvas: React.FC = ({ domainManger }) => {
- const [graphShowType, setGraphShowType] = useState(SemanticNodeType.DIMENSION);
+ // const [graphShowType, setGraphShowType] = useState(SemanticNodeType.DIMENSION);
const { selectDomainId } = domainManger;
return (
@@ -32,15 +32,15 @@ const SemanticGraphCanvas: React.FC
= ({ domainManger }) => {
*/}
- {graphShowType === SemanticNodeType.DATASOURCE ? (
+ {/* {graphShowType === SemanticNodeType.DATASOURCE ? (
- ) : (
-
-
-
- )}
+ ) : ( */}
+
+
+
+ {/* )} */}
);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx
index 9e0a04102..856531da8 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx
@@ -42,7 +42,7 @@ const ClassDimensionTable: React.FC