diff --git a/packages/sqle/src/page/Knowledge/Graph/common/LoadGraph.tsx b/packages/sqle/src/page/Knowledge/Graph/common/LoadGraph.tsx
index 0f85d6a1b..4c26d994f 100644
--- a/packages/sqle/src/page/Knowledge/Graph/common/LoadGraph.tsx
+++ b/packages/sqle/src/page/Knowledge/Graph/common/LoadGraph.tsx
@@ -11,6 +11,7 @@ import {
INodeResponse,
IEdgeResponse
} from '@actiontech/shared/lib/api/sqle/service/common';
+import useThemeStyleData from '../../../../hooks/useThemeStyleData';
type props = {
graphData?: {
@@ -19,18 +20,26 @@ type props = {
};
hoveredNode: string | null;
setHoveredNode: (node: string | null) => void;
+ onLoaded?: () => void;
};
-const LoadGraph: FC
= ({ graphData, hoveredNode, setHoveredNode }) => {
+const LoadGraph: FC = ({
+ graphData,
+ hoveredNode,
+ setHoveredNode,
+ onLoaded
+}) => {
const { createGraph } = useGraph();
const sigma = useSigma();
const registerEvents = useRegisterEvents();
const setSettings = useSetSettings();
const loadGraph = useLoadGraph();
+ const { sharedTheme } = useThemeStyleData();
useEffect(() => {
if (graphData) {
const graph = createGraph(graphData);
loadGraph(graph);
+ onLoaded?.();
const handleEnterNode = (event: any) => {
setHoveredNode(event.node);
@@ -57,7 +66,8 @@ const LoadGraph: FC = ({ graphData, hoveredNode, setHoveredNode }) => {
loadGraph,
registerEvents,
setHoveredNode,
- sigma
+ sigma,
+ onLoaded
]);
useEffect(() => {
@@ -70,19 +80,13 @@ const LoadGraph: FC = ({ graphData, hoveredNode, setHoveredNode }) => {
};
if (hoveredNode && graph.hasNode(hoveredNode)) {
- try {
- const isNeighbor =
- node === hoveredNode ||
- graph.neighbors(hoveredNode).includes(node);
- if (isNeighbor) {
- newData.highlighted = true;
- } else {
- newData.color = '#E2E2E2';
- newData.highlighted = false;
- }
- } catch (error) {
- // 如果出现错误,保持节点原样
- console.debug('Failed to process node highlighting:', error);
+ const isNeighbor =
+ node === hoveredNode || graph.neighbors(hoveredNode).includes(node);
+ if (isNeighbor) {
+ newData.highlighted = true;
+ } else {
+ newData.color = sharedTheme.uiToken.colorFillSecondary;
+ newData.highlighted = false;
}
}
return newData;
@@ -93,20 +97,16 @@ const LoadGraph: FC = ({ graphData, hoveredNode, setHoveredNode }) => {
const newData = { ...data, hidden: false, size: edgeSize };
if (hoveredNode && graph.hasNode(hoveredNode)) {
- try {
- if (graph.extremities(edge).includes(hoveredNode)) {
- newData.size = edgeSize * 1.5;
- } else {
- newData.hidden = true;
- }
- } catch (error) {
- console.debug('Failed to process edge highlighting:', error);
+ if (graph.extremities(edge).includes(hoveredNode)) {
+ newData.size = edgeSize * 1.5;
+ } else {
+ newData.hidden = true;
}
}
return newData;
}
});
- }, [hoveredNode, setSettings, sigma]);
+ }, [hoveredNode, setSettings, sharedTheme.uiToken.colorFillSecondary, sigma]);
return null;
};
diff --git a/packages/sqle/src/page/Knowledge/Graph/common/data.ts b/packages/sqle/src/page/Knowledge/Graph/common/data.ts
index 3c7c4598a..96f73c767 100644
--- a/packages/sqle/src/page/Knowledge/Graph/common/data.ts
+++ b/packages/sqle/src/page/Knowledge/Graph/common/data.ts
@@ -1,6 +1,8 @@
import { SigmaContainerProps } from '@react-sigma/core';
import { EdgeType, NodeType } from '../index.type';
import { Attributes } from 'graphology-types';
+import { NodeProgramType } from 'sigma/rendering';
+import { NodeImageProgram } from '@sigma/node-image';
export const sigmaSettings: SigmaContainerProps<
NodeType,
@@ -9,16 +11,22 @@ export const sigmaSettings: SigmaContainerProps<
>['settings'] = {
// 允许在无效容器中渲染 (默认值: false)
allowInvalidContainer: true,
- defaultNodeType: 'circle',
+ defaultNodeType: 'image',
// 设置边的默认类型 (默认值: 'line')
- defaultEdgeType: 'arrow',
+ defaultEdgeType: 'line',
// 相机缩放比例限制 (默认值: min: 0.1, max: 32)
minCameraRatio: 0.2,
maxCameraRatio: 5,
// 节点渲染程序类 (默认值: {})
- nodeProgramClasses: {},
+ nodeProgramClasses: {
+ image: NodeImageProgram as unknown as NodeProgramType<
+ NodeType,
+ EdgeType,
+ Attributes
+ >
+ },
// 渲染性能优化
hideEdgesOnMove: true, // 移动时隐藏边 (默认值: false)
@@ -26,8 +34,13 @@ export const sigmaSettings: SigmaContainerProps<
renderLabels: true, // 是否渲染标签 (默认值: true)
renderEdgeLabels: false, // 是否渲染边标签 (默认值: false)
+ labelFont: `'PlusJakartaSans Medium', -apple-system, 'Microsoft YaHei',
+ BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
+ sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
+ 'Noto Color Emoji`,
+ labelSize: 14,
// 标签渲染优化
- labelDensity: 0.1, // 标签密度 (默认值: 0.03)
+ labelDensity: 0.07, // 标签密度 (默认值: 0.03)
labelGridCellSize: 100, // 标签网格大小 (默认值: 100)
labelRenderedSizeThreshold: 8, // 标签大小阈值 (默认值: 6)
diff --git a/packages/sqle/src/page/Knowledge/Graph/hooks/useGraph.ts b/packages/sqle/src/page/Knowledge/Graph/hooks/useGraph.ts
index 03ca295f1..459821972 100644
--- a/packages/sqle/src/page/Knowledge/Graph/hooks/useGraph.ts
+++ b/packages/sqle/src/page/Knowledge/Graph/hooks/useGraph.ts
@@ -5,9 +5,11 @@ import {
INodeResponse
} from '@actiontech/shared/lib/api/sqle/service/common';
import { EdgeType, NodeType } from '../index.type';
+import useThemeStyleData from '../../../../hooks/useThemeStyleData';
const useGraph = () => {
- const randomColor = useCallback(() => {
+ const { sqleTheme } = useThemeStyleData();
+ const generateNodeColor = useCallback(() => {
const digits = '0123456789abcdef';
let code = '#';
for (let i = 0; i < 6; i++) {
@@ -72,10 +74,11 @@ const useGraph = () => {
graph.addNode(node.id, {
label: node.name ?? '',
size: normalizedSize,
- color: randomColor(),
+ color: generateNodeColor(),
x: 0.5 + radius * Math.cos(theta),
y: 0.5 + radius * Math.sin(theta),
- shortLabel: (node.name ?? '')[0]?.toUpperCase() ?? ''
+ shortLabel: (node.name ?? '')[0]?.toUpperCase() ?? '',
+ image: '/static/image/knowledge.svg'
});
});
@@ -90,13 +93,19 @@ const useGraph = () => {
graph.addEdge(edge.from_id, edge.to_id, {
size: normalizedSize,
- forceLabel: false
+ forceLabel: false,
+ color: sqleTheme.knowledgeTheme.graph.edge.color
});
});
return graph as Graph;
},
- [calculateNormalizedSize, getWeightRange, randomColor]
+ [
+ getWeightRange,
+ calculateNormalizedSize,
+ generateNodeColor,
+ sqleTheme.knowledgeTheme.graph.edge.color
+ ]
);
return { createGraph };
diff --git a/packages/sqle/src/page/Knowledge/Graph/index.tsx b/packages/sqle/src/page/Knowledge/Graph/index.tsx
index fca1a55a1..e47885914 100644
--- a/packages/sqle/src/page/Knowledge/Graph/index.tsx
+++ b/packages/sqle/src/page/Knowledge/Graph/index.tsx
@@ -16,13 +16,16 @@ import { KnowledgeGraphStyleWrapper } from './style';
import { KnowledgeGraphProp } from './index.type';
import classNames from 'classnames';
import NodePopover from './common/NodePopover';
+import { Spin } from 'antd';
+import { useTranslation } from 'react-i18next';
const KnowledgeGraph: React.FC = ({ graphData }) => {
+ const { t } = useTranslation();
const [selectedNode, setSelectedNode] = useState(null);
const [focusNode, setFocusNode] = useState(null);
const [isFullScreen, setIsFullScreen] = useState(false);
const [hoveredNode, setHoveredNode] = useState(null);
- const [isNodeHovered, setIsNodeHovered] = useState(false);
+ const [isLoading, setIsLoading] = useState(true);
const onFocus = useCallback((value: GraphSearchOption | null) => {
if (value === null) setFocusNode(null);
@@ -34,6 +37,10 @@ const KnowledgeGraph: React.FC = ({ graphData }) => {
else if (value.type === 'nodes') setSelectedNode(value.id);
}, []);
+ const handleGraphLoaded = useCallback(() => {
+ setIsLoading(false);
+ }, []);
+
const postSearchResult = useCallback(
(options: GraphSearchOption[]): GraphSearchOption[] => {
return options.length <= 10
@@ -66,42 +73,45 @@ const KnowledgeGraph: React.FC = ({ graphData }) => {
return (
-
-
-
-
+
-
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/packages/sqle/src/page/Knowledge/Graph/index.type.ts b/packages/sqle/src/page/Knowledge/Graph/index.type.ts
index 7174ec217..be97da93e 100644
--- a/packages/sqle/src/page/Knowledge/Graph/index.type.ts
+++ b/packages/sqle/src/page/Knowledge/Graph/index.type.ts
@@ -12,6 +12,7 @@ export interface NodeType {
color: string;
shortLabel: string;
forceLabel?: boolean;
+ image?: string;
}
export interface EdgeType {
size: number;
diff --git a/packages/sqle/src/page/Knowledge/Graph/style.ts b/packages/sqle/src/page/Knowledge/Graph/style.ts
index 0b64a42f7..86d14580a 100644
--- a/packages/sqle/src/page/Knowledge/Graph/style.ts
+++ b/packages/sqle/src/page/Knowledge/Graph/style.ts
@@ -2,22 +2,40 @@ import { styled } from '@mui/material';
export const KnowledgeGraphStyleWrapper = styled('div')`
width: 100%;
- height: calc(100% - 140px);
+ height: calc(100% - 124px);
padding: 12px;
margin-top: 20px;
border-radius: 10px;
border: 1px solid
- ${({ theme }) => theme.sharedTheme.uiToken.colorBorderSecondary};
-
- background: linear-gradient(120deg, #fdfbfb 0%, #f9f7f7 100%),
- radial-gradient(circle at 50% 50%, #ffffff 0%, transparent 100%);
+ ${({ theme }) => theme.sqleTheme.knowledgeTheme.graph.wrapper.borderColor};
background-blend-mode: soft-light;
-
- background-image: radial-gradient(#f0f0f0 1px, transparent 1px),
- radial-gradient(#f0f0f0 1px, transparent 1px);
+ background-image: radial-gradient(
+ ${({ theme }) => theme.sqleTheme.knowledgeTheme.graph.wrapper.dotColor}
+ 1px,
+ ${({ theme }) =>
+ theme.sqleTheme.knowledgeTheme.graph.wrapper.backgroundColor}
+ 1px
+ ),
+ radial-gradient(
+ ${({ theme }) => theme.sqleTheme.knowledgeTheme.graph.wrapper.dotColor}
+ 1px,
+ ${({ theme }) =>
+ theme.sqleTheme.knowledgeTheme.graph.wrapper.backgroundColor}
+ 1px
+ );
background-size: 20px 20px;
background-position: 0 0, 10px 10px;
+ .ant-spin-nested-loading {
+ width: 100%;
+ height: 100%;
+
+ .ant-spin-container {
+ width: 100%;
+ height: 100%;
+ }
+ }
+
.sigma-container {
height: 100%;
width: 100%;
@@ -25,11 +43,12 @@ export const KnowledgeGraphStyleWrapper = styled('div')`
}
.sigma-container-full-screen {
- background: ${({ theme }) => theme.sharedTheme.uiToken.colorBgBase};
+ background: ${({ theme }) =>
+ theme.sqleTheme.knowledgeTheme.graph.container.fullScreenBgColor};
}
.graph-control-container {
border: 1px solid
- ${({ theme }) => theme.sharedTheme.uiToken.colorBorderSecondary};
+ ${({ theme }) => theme.sqleTheme.knowledgeTheme.graph.control.borderColor};
}
`;
diff --git a/packages/sqle/src/page/Knowledge/RefineResults/index.tsx b/packages/sqle/src/page/Knowledge/RefineResults/index.tsx
index 9d8d3c9a8..3e1081e70 100644
--- a/packages/sqle/src/page/Knowledge/RefineResults/index.tsx
+++ b/packages/sqle/src/page/Knowledge/RefineResults/index.tsx
@@ -113,18 +113,16 @@ const KnowledgeSearchResults: React.FC = () => {
{
- if (searchText || selectedTags.length) {
- startSearch({
- searchText: searchText,
- selectedTags: selectedTags,
- pageIndex: 1,
- pageSize: pagination.page_size
- });
- setPagination({
- page_index: 1,
- page_size: pagination.page_size
- });
- }
+ startSearch({
+ searchText: searchText,
+ selectedTags: selectedTags,
+ pageIndex: 1,
+ pageSize: pagination.page_size
+ });
+ setPagination({
+ page_index: 1,
+ page_size: pagination.page_size
+ });
}}
searchText={searchText}
selectedTags={selectedTags}
diff --git a/packages/sqle/src/page/Knowledge/index.ee.tsx b/packages/sqle/src/page/Knowledge/index.ee.tsx
new file mode 100644
index 000000000..02b6bfd90
--- /dev/null
+++ b/packages/sqle/src/page/Knowledge/index.ee.tsx
@@ -0,0 +1,47 @@
+import { PageHeader, useTypedNavigate } from '@actiontech/shared';
+import { useTranslation } from 'react-i18next';
+import { KnowledgeStyleWrapper } from './style';
+import KnowledgeSearchBar from './Common/KnowledgeSearchBar';
+import { KnowledgeSearchBarProps } from './Common/KnowledgeSearchBar/index.type';
+import { ROUTE_PATHS } from '@actiontech/shared/lib/data/routePaths';
+import KnowledgeGraph from './Graph';
+import { KnowledgeService } from '@actiontech/shared/lib/api';
+import { useRequest } from 'ahooks';
+import { ResponseCode } from '@actiontech/shared/lib/enum';
+
+const KnowledgeEE: React.FC = () => {
+ const { t } = useTranslation();
+
+ const navigate = useTypedNavigate();
+
+ const onSearch: KnowledgeSearchBarProps['onSearch'] = ({ searchText }) => {
+ navigate(ROUTE_PATHS.SQLE.KNOWLEDGE.refined, {
+ queries: { keywords: searchText }
+ });
+ };
+
+ const { data: graphData } = useRequest(() =>
+ KnowledgeService.getKnowledgeGraph().then((res) => {
+ if (res.data.code === ResponseCode.SUCCESS) {
+ return {
+ edges: res.data.data?.edges ?? [],
+ nodes: res.data.data?.nodes ?? []
+ };
+ }
+ })
+ );
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default KnowledgeEE;
diff --git a/packages/sqle/src/page/Knowledge/index.tsx b/packages/sqle/src/page/Knowledge/index.tsx
index 5e3d70dcd..efc64fddd 100644
--- a/packages/sqle/src/page/Knowledge/index.tsx
+++ b/packages/sqle/src/page/Knowledge/index.tsx
@@ -1,46 +1,22 @@
-import { PageHeader, useTypedNavigate } from '@actiontech/shared';
+import { Typography } from 'antd';
import { useTranslation } from 'react-i18next';
-import { KnowledgeStyleWrapper } from './style';
-import KnowledgeSearchBar from './Common/KnowledgeSearchBar';
-import { KnowledgeSearchBarProps } from './Common/KnowledgeSearchBar/index.type';
-import { ROUTE_PATHS } from '@actiontech/shared/lib/data/routePaths';
-import KnowledgeGraph from './Graph';
-import { KnowledgeService } from '@actiontech/shared/lib/api';
-import { useRequest } from 'ahooks';
-import { ResponseCode } from '@actiontech/shared/lib/enum';
+import { EnterpriseFeatureDisplay } from '@actiontech/shared';
+import KnowledgeEE from './index.ee';
-const Knowledge: React.FC = () => {
+const Knowledge = () => {
const { t } = useTranslation();
- const navigate = useTypedNavigate();
-
- const onSearch: KnowledgeSearchBarProps['onSearch'] = ({ searchText }) => {
- navigate(ROUTE_PATHS.SQLE.KNOWLEDGE.refined, {
- queries: { keywords: searchText }
- });
- };
-
- const { data: graphData } = useRequest(() =>
- KnowledgeService.getKnowledgeGraph().then((res) => {
- if (res.data.code === ResponseCode.SUCCESS) {
- return {
- edges: res.data.data?.edges ?? [],
- nodes: res.data.data?.nodes ?? []
- };
- }
- })
- );
-
return (
- <>
-
-
-
-
-
-
-
- >
+
+ {t('knowledgeBase.ceTips')}
+
+ }
+ >
+
+
);
};
diff --git a/packages/sqle/src/router/config.tsx b/packages/sqle/src/router/config.tsx
index 76c315498..2b6feefcc 100644
--- a/packages/sqle/src/router/config.tsx
+++ b/packages/sqle/src/router/config.tsx
@@ -110,11 +110,11 @@ const WorkflowSqlAnalyze = React.lazy(
() => import('../page/SqlAnalyze/Workflow')
);
+const Knowledge = React.lazy(() => import('../page/Knowledge/index'));
+
// #if [ee]
const RuleKnowledge = React.lazy(() => import('../page/RuleKnowledge'));
-const Knowledge = React.lazy(() => import('../page/Knowledge'));
-
const KnowledgeRefineResults = React.lazy(
() => import('../page/Knowledge/RefineResults')
);
@@ -570,24 +570,28 @@ export const globalRouterConfig: RouterConfigItem[] = [
}
]
},
- // #if [ee]
{
path: ROUTE_PATHS.SQLE.KNOWLEDGE.index.path,
key: 'knowledge',
+ // #if [ee]
permission: PERMISSIONS.PAGES.SQLE.KNOWLEDGE,
+ // #endif
children: [
{
index: true,
key: 'knowledgeIndex',
element:
},
+ // #if [ee]
{
path: ROUTE_PATHS.SQLE.KNOWLEDGE.refined.path,
key: 'knowledgeRefinedResults',
element:
}
+ // #endif
]
},
+ // #if [ee]
{
path: ROUTE_PATHS.SQLE.RULE_KNOWLEDGE.index.path,
key: 'ruleKnowledge',
diff --git a/packages/sqle/src/theme/dark/knowledge.ts b/packages/sqle/src/theme/dark/knowledge.ts
index 7195c5664..74c0e7c1b 100644
--- a/packages/sqle/src/theme/dark/knowledge.ts
+++ b/packages/sqle/src/theme/dark/knowledge.ts
@@ -15,5 +15,21 @@ export const knowledgeTheme: KnowledgeTheme = {
description: {
borderLeftColor: darkThemeUI.uiToken.colorSuccess
}
+ },
+ graph: {
+ wrapper: {
+ dotColor: '#f0f0f0',
+ backgroundColor: 'transparent',
+ borderColor: darkThemeUI.uiToken.colorBorderSecondary
+ },
+ container: {
+ fullScreenBgColor: darkThemeUI.uiToken.colorBgBase
+ },
+ control: {
+ borderColor: darkThemeUI.uiToken.colorBorderSecondary
+ },
+ edge: {
+ color: 'rgba(96, 96, 96, 0.6)'
+ }
}
};
diff --git a/packages/sqle/src/theme/light/knowledge.ts b/packages/sqle/src/theme/light/knowledge.ts
index 20532bf8a..e47f7139c 100644
--- a/packages/sqle/src/theme/light/knowledge.ts
+++ b/packages/sqle/src/theme/light/knowledge.ts
@@ -15,5 +15,21 @@ export const knowledgeTheme: KnowledgeTheme = {
description: {
borderLeftColor: lightThemeUI.uiToken.colorSuccess
}
+ },
+ graph: {
+ wrapper: {
+ dotColor: '#f0f0f0',
+ backgroundColor: 'transparent',
+ borderColor: lightThemeUI.uiToken.colorBorderSecondary
+ },
+ container: {
+ fullScreenBgColor: lightThemeUI.uiToken.colorBgBase
+ },
+ control: {
+ borderColor: lightThemeUI.uiToken.colorBorderSecondary
+ },
+ edge: {
+ color: '#ccc'
+ }
}
};
diff --git a/packages/sqle/src/theme/type.ts b/packages/sqle/src/theme/type.ts
index dd4b79024..bd14ea1eb 100644
--- a/packages/sqle/src/theme/type.ts
+++ b/packages/sqle/src/theme/type.ts
@@ -319,4 +319,20 @@ export type KnowledgeTheme = {
borderLeftColor: string;
};
};
+ graph: {
+ wrapper: {
+ borderColor: string;
+ backgroundColor: string;
+ dotColor: string;
+ };
+ container: {
+ fullScreenBgColor: string;
+ };
+ control: {
+ borderColor: string;
+ };
+ edge: {
+ color: string;
+ };
+ };
};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 92fff77b6..4343ee10f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -335,6 +335,9 @@ importers:
'@react-sigma/layout-force':
specifier: ^5.0.2
version: 5.0.2(graphology-layout-force@0.2.4)(graphology@0.25.4)(react@18.2.0)(sigma@3.0.1)
+ '@sigma/node-image':
+ specifier: ^3.0.0
+ version: 3.0.0(sigma@3.0.1)
'@uiw/react-md-editor':
specifier: ^3.23.5
version: 3.23.5(@types/react@18.0.15)(react-dom@18.2.0)(react@18.2.0)
@@ -4693,6 +4696,14 @@ packages:
selderee: 0.11.0
dev: true
+ /@sigma/node-image@3.0.0(sigma@3.0.1):
+ resolution: {integrity: sha512-i4WLNPugDY4jgQEZtNSiSVj4HHXOraciXLtlgdygeUxMVEhH8PJ/+Q1vQ9f/SlKFnZQ+7vH3HnsSDW6FD9aP+g==}
+ peerDependencies:
+ sigma: '>=3.0.0-beta.10'
+ dependencies:
+ sigma: 3.0.1(graphology-types@0.24.8)
+ dev: false
+
/@sinclair/typebox@0.27.8:
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
dev: true