From 693fc03c6ff976c38a5cf4381f3ba9fe576e6a08 Mon Sep 17 00:00:00 2001 From: tristanliu Date: Wed, 14 Jun 2023 19:31:43 +0800 Subject: [PATCH] [improvement](semantic-fe) add dimension&datasource relation graph and fix create datasource params bug --- .../RightContent/AvatarDropdown.tsx | 2 +- .../src/components/TMEAvatar/index.tsx | 12 +- .../supersonic-fe/src/pages/Login/index.tsx | 4 +- .../components/DataSourceFieldForm.tsx | 7 +- .../SemanticGraph/components/ToolBar.tsx | 182 ++++--- .../SemanticGraph/components/ToolTips.tsx | 73 +++ .../SemanticModel/SemanticGraph/index.tsx | 498 ++++++++++-------- 7 files changed, 491 insertions(+), 287 deletions(-) create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolTips.tsx diff --git a/webapp/packages/supersonic-fe/src/components/RightContent/AvatarDropdown.tsx b/webapp/packages/supersonic-fe/src/components/RightContent/AvatarDropdown.tsx index 2f3d5aaff..c5d445af9 100644 --- a/webapp/packages/supersonic-fe/src/components/RightContent/AvatarDropdown.tsx +++ b/webapp/packages/supersonic-fe/src/components/RightContent/AvatarDropdown.tsx @@ -40,7 +40,7 @@ const AvatarDropdown: React.FC = () => { }; const { currentUser = {} } = initialState as any; - console.log(currentUser, 'currentUser'); + const menuHeaderDropdown = ( diff --git a/webapp/packages/supersonic-fe/src/components/TMEAvatar/index.tsx b/webapp/packages/supersonic-fe/src/components/TMEAvatar/index.tsx index 7fb461a03..45c653e24 100644 --- a/webapp/packages/supersonic-fe/src/components/TMEAvatar/index.tsx +++ b/webapp/packages/supersonic-fe/src/components/TMEAvatar/index.tsx @@ -7,9 +7,11 @@ interface Props extends AvatarProps { staffName?: string; avatarImg?: string; } - -const TMEAvatar: FC = ({ avatarImg, ...restProps }) => ( - } {...restProps} /> -); - +const { tmeAvatarUrl } = process.env; +const TMEAvatar: FC = ({ staffName, avatarImg, ...restProps }) => { + const avatarSrc = tmeAvatarUrl ? `${tmeAvatarUrl}${staffName}.png` : avatarImg; + return ( + } {...restProps} /> + ); +}; export default TMEAvatar; diff --git a/webapp/packages/supersonic-fe/src/pages/Login/index.tsx b/webapp/packages/supersonic-fe/src/pages/Login/index.tsx index f2cafad0a..b4c05b119 100644 --- a/webapp/packages/supersonic-fe/src/pages/Login/index.tsx +++ b/webapp/packages/supersonic-fe/src/pages/Login/index.tsx @@ -12,7 +12,7 @@ import { postUserLogin, userRegister } from './services'; import { AUTH_TOKEN_KEY } from '@/common/constants'; import { queryCurrentUser } from '@/services/user'; import { history, useModel } from 'umi'; -import { setToken as setChatSdkToken } from 'supersonic-chat-sdk'; +// import { setToken as setChatSdkToken } from 'supersonic-chat-sdk'; const { Item } = Form; const LoginPage: React.FC = () => { @@ -25,7 +25,7 @@ const LoginPage: React.FC = () => { const { code, data, msg } = await postUserLogin(values); if (code === 200) { localStorage.setItem(AUTH_TOKEN_KEY, data); - setChatSdkToken(data || ''); + // setChatSdkToken(data || ''); const { code: queryUserCode, data: queryUserData } = await queryCurrentUser(); if (queryUserCode === 200) { const currentUser = { diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceFieldForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceFieldForm.tsx index 28a093301..0dcdf19a4 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceFieldForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceFieldForm.tsx @@ -167,7 +167,12 @@ const FieldForm: React.FC = ({ fields, onFieldChange }) => { disabled={!editState} onChange={(e) => { const value = e.target.value; - handleFieldChange(record, 'name', value); + // handleFieldChange(record, 'name', value); + onFieldChange(record.bizName, { + ...record, + name: value, + [isCreateName]: 1, + }); }} placeholder="请输入中文名" /> 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 22770ae1d..fb3a600e0 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,82 +1,140 @@ import G6 from '@antv/g6'; -// import { modifyCSS, createDom } from '@antv/dom-util'; import { createDom } from '@antv/dom-util'; -const initToolBar = () => { - // const defaultConfig = G6.ToolBar - const toolBarInstance = new G6.ToolBar(); +const searchIconSvgPath = ``; + +const searchNode = (graph) => { + const toolBarSearchInput = document.getElementById('toolBarSearchInput') as HTMLInputElement; + const searchText = toolBarSearchInput.value.trim(); + let lastFoundNode = null; + graph.getNodes().forEach((node) => { + const model = node.getModel(); + const isFound = searchText && model.label.includes(searchText); + if (isFound) { + graph.setItemState(node, 'active', true); + lastFoundNode = node; + } else { + graph.setItemState(node, 'active', false); + } + }); + + if (lastFoundNode) { + // 将视图移动到找到的节点位置 + graph.focusItem(lastFoundNode, true, { + duration: 300, + easing: 'easeCubic', + }); + } +}; + +const generatorSearchInputDom = (graph) => { + const domString = + ''; + const searchInputDom = createDom(domString); + searchInputDom.addEventListener('keydown', (event) => { + if (event.key === 'Enter') { + searchNode(graph); + } + }); + return searchInputDom; +}; + +const generatorSearchBtnDom = (graph) => { + const domString = ``; + const searchBtnDom = createDom(domString); + searchBtnDom.addEventListener('click', () => { + searchNode(graph); + }); + return searchBtnDom; +}; + +const searchInputDOM = (graph) => { + const searchInputDom = generatorSearchInputDom(graph); + const searchBtnDom = generatorSearchBtnDom(graph); + const searchInput = ` + `; + const searchDom = createDom(searchInput); + const searchWrapperDom = searchDom.querySelector('#toolBarSearchWrapper'); + searchWrapperDom.insertBefore(searchInputDom, searchWrapperDom.firstChild); + searchWrapperDom.querySelector('.ant-input-group-addon').appendChild(searchBtnDom); + return searchDom; +}; + +const initToolBar = () => { + const toolBarInstance = new G6.ToolBar(); const config = toolBarInstance._cfgs; const defaultContentDomString = config.getContent(); - // const regex = /]*>|<\/ul>/g; - // const innerDom = defaultContentDom.replace(regex, ''); const defaultContentDom = createDom(defaultContentDomString); - // @ts-ignore const elements = defaultContentDom.querySelectorAll('li[code="redo"], li[code="undo"]'); elements.forEach((element) => { element.remove(); }); - const searchBtnDom = `
  • - -
  • `; + +`; + defaultContentDom.insertAdjacentHTML('afterbegin', searchBtnDom); + let searchInputContentVisible = false; const toolbar = new G6.ToolBar({ position: { x: 10, y: 10 }, - getContent: () => { - return `${searchBtnDom}${defaultContentDom}`; + className: 'semantic-graph-toolbar', + getContent: (graph) => { + const searchInput = searchInputDOM(graph); + const content = `
    ${defaultContentDom.outerHTML}
    `; + const contentDom = createDom(content); + contentDom.appendChild(searchInput); + return contentDom; + }, + handleClick: (code, graph) => { + if (code === 'search') { + const searchText = document.getElementById('searchInputContent'); + if (searchText) { + const visible = searchInputContentVisible ? 'none' : 'block'; + searchText.style.display = visible; + searchInputContentVisible = !searchInputContentVisible; + } + } else { + // handleDefaultOperator public方法缺失graph作为参数传入,将graph挂载在cfgs上,源码通过get会获取到graph,完成默认code的执行逻辑 + toolBarInstance._cfgs.graph = graph; + toolBarInstance.handleDefaultOperator(code); + } }, }); - // const toolbar = new G6.ToolBar({ - // getContent: (graph) => { - // const searchInput = document.createElement('input'); - // searchInput.id = 'search-input'; - // searchInput.placeholder = '搜索节点'; - // const searchBtn = document.createElement('button'); - // searchBtn.id = 'search-btn'; - // searchBtn.innerHTML = '搜索'; - - // const container = document.createElement('div'); - // container.appendChild(searchInput); - // container.appendChild(searchBtn); - // return container; - // }, - // handleClick: (name, graph) => { - // if (name === 'search-btn') { - // const searchText = document.getElementById('search-input').value.trim(); - // if (!searchText) { - // return; - // } - - // const foundNode = graph.getNodes().find((node) => { - // const model = node.getModel(); - // return model.label === searchText; - // }); - - // if (foundNode) { - // // 如果找到了节点,将其设置为选中状态 - // graph.setItemState(foundNode, 'active', true); - // // 将视图移动到找到的节点位置 - // graph.focusItem(foundNode, true, { - // duration: 300, - // easing: 'easeCubic', - // }); - // } else { - // alert('未找到节点'); - // } - // } - // }, - // }); return toolbar; }; export default initToolBar; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolTips.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolTips.tsx new file mode 100644 index 000000000..64e195327 --- /dev/null +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolTips.tsx @@ -0,0 +1,73 @@ +import G6 from '@antv/g6'; +import moment from 'moment'; + +const initTooltips = () => { + const tooltip = new G6.Tooltip({ + offsetX: 10, + offsetY: 10, + fixToNode: [1, 0.5], + // the types of items that allow the tooltip show up + // 允许出现 tooltip 的 item 类型 + // itemTypes: ['node', 'edge'], + itemTypes: ['node'], + // custom the tooltip's content + // 自定义 tooltip 内容 + getContent: (e) => { + const outDiv = document.createElement('div'); + outDiv.style.width = 'fit-content'; + outDiv.style.height = 'fit-content'; + const model = e.item.getModel(); + console.log(model, e.item, 'model'); + const { name, bizName, createdBy, updatedAt, description } = model; + const list = [ + { + label: '名称:', + value: name, + }, + { + label: '字段:', + value: bizName, + }, + { + label: '创建人:', + value: createdBy, + }, + { + label: '更新时间:', + value: updatedAt ? moment(updatedAt).format('YYYY-MM-DD HH:mm:ss') : '', + }, + { + label: '描述:', + value: description, + }, + ]; + const listHtml = list.reduce((htmlString, item) => { + const { label, value } = item; + if (value) { + htmlString += `

    + ${label} + ${value} +

    `; + } + return htmlString; + }, ''); + const html = `
    + ${listHtml} +
    `; + if (e.item.getType() === 'node') { + outDiv.innerHTML = html; + } + // else { + // const source = e.item.getSource(); + // const target = e.item.getTarget(); + // outDiv.innerHTML = `来源:${source.getModel().name}
    去向:${ + // target.getModel().name + // }`; + // } + return outDiv; + }, + }); + + return tooltip; +}; +export default initTooltips; 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 bc369f6c7..6f79cdca8 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx @@ -3,9 +3,10 @@ import { connect } from 'umi'; import type { StateType } from '../model'; import type { Dispatch } from 'umi'; import { typeConfigs } from './utils'; -import { message } from 'antd'; +import { message, Row, Col, Radio } from 'antd'; import { getDatasourceList, getDomainSchemaRela } from '../service'; import initToolBar from './components/ToolBar'; +import initTooltips from './components/ToolTips'; import G6 from '@antv/g6'; type Props = { @@ -17,11 +18,13 @@ type Props = { const DomainManger: React.FC = ({ domainManger, domainId }) => { const ref = useRef(null); const [graphData, setGraphData] = useState({}); + const [dataSourceListData, setDataSourceListData] = useState([]); + const [graphShowType, setGraphShowType] = useState('dimension'); const legendDataRef = useRef([]); const graphRef = useRef(null); const legendDataFilterFunctions = useRef({}); - const { dimensionList } = domainManger; + // const { dimensionList } = domainManger; const toggleNodeVisibility = (graph, node, visible) => { if (visible) { @@ -42,37 +45,80 @@ const DomainManger: React.FC = ({ domainManger, domainId }) => { } }; - const formatterRelationData = (dataSourceList: any[]) => { - const relationData = dataSourceList.reduce((relationList: any[], item: any) => { - const { id, name } = item; - const dataSourceId = `dataSource-${id}`; - const dimensionChildrenList = dimensionList.reduce( - (dimensionChildren: any[], dimension: any) => { - const { id: dimensionId, name: dimensionName, datasourceId } = dimension; - if (datasourceId === id) { - dimensionChildren.push({ - nodeType: 'dimension', - legendType: dataSourceId, - id: `dimension-${dimensionId}`, - name: dimensionName, - style: { - lineWidth: 2, - fill: '#f0f7ff', - stroke: '#a6ccff', - }, - }); - } - return dimensionChildren; + const getDimensionChildren = (dimensions: any[], dataSourceId: string) => { + const dimensionChildrenList = dimensions.reduce((dimensionChildren: any[], dimension: any) => { + const { + id: dimensionId, + name: dimensionName, + bizName, + description, + createdBy, + updatedAt, + } = dimension; + // if (datasourceId === id) { + dimensionChildren.push({ + nodeType: 'dimension', + legendType: dataSourceId, + id: `dimension-${dimensionId}`, + name: dimensionName, + bizName, + description, + createdBy, + updatedAt, + style: { + lineWidth: 2, + fill: '#f0f7ff', + stroke: '#a6ccff', }, - [], - ); + }); + // } + return dimensionChildren; + }, []); + return dimensionChildrenList; + }; + + const getMetricChildren = (metrics: any[], dataSourceId: string) => { + const metricsChildrenList = metrics.reduce((metricsChildren: any[], dimension: any) => { + const { id, name, bizName, description, createdBy, updatedAt } = dimension; + metricsChildren.push({ + nodeType: 'metric', + legendType: dataSourceId, + id: `dimension-${id}`, + name, + bizName, + description, + createdBy, + updatedAt, + style: { + lineWidth: 2, + fill: '#f0f7ff', + stroke: '#a6ccff', + }, + }); + return metricsChildren; + }, []); + return metricsChildrenList; + }; + + const formatterRelationData = (dataSourceList: any[], type = graphShowType) => { + const relationData = dataSourceList.reduce((relationList: any[], item: any) => { + const { datasource, dimensions, metrics } = item; + const { id, name } = datasource; + const dataSourceId = `dataSource-${id}`; + let childrenList = []; + if (type === 'metirc') { + childrenList = getMetricChildren(metrics, dataSourceId); + } + if (type === 'dimension') { + childrenList = getDimensionChildren(dimensions, dataSourceId); + } relationList.push({ name, legendType: dataSourceId, id: dataSourceId, nodeType: 'datasource', size: 40, - children: [...dimensionChildrenList], + children: [...childrenList], style: { lineWidth: 2, fill: '#BDEFDB', @@ -84,26 +130,33 @@ const DomainManger: React.FC = ({ domainManger, domainId }) => { return relationData; }; + const changeGraphData = (data: any, type?: string) => { + const relationData = formatterRelationData(data, type); + const legendList = relationData.map((item: any) => { + const { id, name } = item; + return { + id, + label: name, + order: 4, + ...typeConfigs.datasource, + }; + }); + legendDataRef.current = legendList; + const graphRootData = { + id: 'root', + name: domainManger.selectDomainName, + children: relationData, + }; + setGraphData(graphRootData); + }; + const queryDataSourceList = async (params: any) => { - getDomainSchemaRela(params.domainId); - const { code, data, msg } = await getDatasourceList({ ...params }); + const { code, data, msg } = await getDomainSchemaRela(params.domainId); if (code === 200) { - const relationData = formatterRelationData(data); - const legendList = relationData.map((item: any) => { - const { id, name } = item; - return { - id, - label: name, - order: 4, - ...typeConfigs.datasource, - }; - }); - legendDataRef.current = legendList; - setGraphData({ - id: 'root', - name: domainManger.selectDomainName, - children: relationData, - }); + if (data) { + changeGraphData(data); + setDataSourceListData(data); + } } else { message.error(msg); } @@ -111,7 +164,7 @@ const DomainManger: React.FC = ({ domainManger, domainId }) => { useEffect(() => { queryDataSourceList({ domainId }); - }, []); + }, [domainId]); const getLegendDataFilterFunctions = () => { legendDataRef.current.map((item: any) => { @@ -143,201 +196,214 @@ const DomainManger: React.FC = ({ domainManger, domainId }) => { }; // const [visible, setVisible] = useState(false); useEffect(() => { + console.log(domainId, graphData, 'domainId'); if (!(Array.isArray(graphData.children) && graphData.children.length > 0)) { return; } + const container = document.getElementById('semanticGraph'); const width = container!.scrollWidth; const height = container!.scrollHeight || 500; - if (!graphRef.current) { - getLegendDataFilterFunctions(); + // if (!graphRef.current) { + getLegendDataFilterFunctions(); - const toolbar = initToolBar(); - // const toolbar = new G6.ToolBar({ - // getContent: (graph) => { - // const searchIcon = document.createElement('i'); - // searchIcon.className = 'g6-toolbar-search-icon'; - // searchIcon.style.cssText = ` - // display: inline-block; - // width: 16px; - // height: 16px; - // background-image: url(https://gw.alipayobjects.com/zos/rmsportal/wzQIcOMRTkQwFgaaDIFs.svg); - // background-size: 16px 16px; - // margin-right: 8px; - // cursor: pointer; - // `; - - // searchIcon.addEventListener('click', () => { - // setVisible((prevVisible) => !prevVisible); - // }); - - // const ul = document.createElement('ul'); - // ul.className = 'g6-component-toolbar'; - // ul.appendChild(searchIcon); - - // return ul; - // }, - // }); - - const tooltip = new G6.Tooltip({ - offsetX: 10, - offsetY: 10, - fixToNode: [1, 0.5], - // the types of items that allow the tooltip show up - // 允许出现 tooltip 的 item 类型 - // itemTypes: ['node', 'edge'], - itemTypes: ['node'], - // custom the tooltip's content - // 自定义 tooltip 内容 - getContent: (e) => { - const outDiv = document.createElement('div'); - outDiv.style.width = 'fit-content'; - outDiv.style.height = 'fit-content'; - const model = e.item.getModel(); - if (e.item.getType() === 'node') { - outDiv.innerHTML = `${model.name}`; - } - // else { - // const source = e.item.getSource(); - // const target = e.item.getTarget(); - // outDiv.innerHTML = `来源:${source.getModel().name}
    去向:${ - // target.getModel().name - // }`; - // } - return outDiv; + const toolbar = initToolBar(); + const tooltip = initTooltips(); + const legend = new G6.Legend({ + // container: 'legendContainer', + data: { + nodes: legendDataRef.current, + }, + align: 'center', + layout: 'horizontal', // vertical + position: 'bottom-right', + vertiSep: 12, + horiSep: 24, + offsetY: -24, + padding: [4, 16, 8, 16], + containerStyle: { + fill: '#ccc', + lineWidth: 1, + }, + title: '可见数据源', + titleConfig: { + position: 'center', + offsetX: 0, + offsetY: 12, + style: { + fontSize: 12, + fontWeight: 500, + fill: '#000', }, - }); - const legend = new G6.Legend({ - // container: 'legendContainer', - data: { - nodes: legendDataRef.current, + }, + filter: { + enable: true, + multiple: true, + trigger: 'click', + graphActiveState: 'activeByLegend', + graphInactiveState: 'inactiveByLegend', + filterFunctions: { + ...legendDataFilterFunctions.current, }, - align: 'center', - layout: 'horizontal', // vertical - position: 'bottom-right', - vertiSep: 12, - horiSep: 24, - offsetY: -24, - padding: [4, 16, 8, 16], - containerStyle: { - fill: '#ccc', - lineWidth: 1, - }, - title: '可见数据源', - titleConfig: { - position: 'center', - offsetX: 0, - offsetY: 12, + }, + }); + // 我使用TreeGraph进行layout布局,采用{type: 'compactBox',direction: 'LR'}模式,如何使子节点与根节点的连线只连接到上下连接桩上 + + graphRef.current = new G6.TreeGraph({ + container: 'semanticGraph', + width, + height, + linkCenter: true, + modes: { + default: [ + { + type: 'collapse-expand', + onChange: function onChange(item, collapsed) { + const data = item.get('model'); + data.collapsed = collapsed; + return true; + }, + }, + 'drag-node', + 'drag-canvas', + // 'activate-relations', + 'zoom-canvas', + { + type: 'activate-relations', + trigger: 'mouseenter', // 触发方式,可以是 'mouseenter' 或 'click' + resetSelected: true, // 点击空白处时,是否取消高亮 + }, + ], + }, + defaultNode: { + size: 26, + labelCfg: { + position: 'right', + offset: 5, style: { - fontSize: 12, - fontWeight: 500, - fill: '#000', + stroke: '#fff', + lineWidth: 4, }, }, - filter: { - enable: true, - multiple: true, - trigger: 'click', - graphActiveState: 'activeByLegend', - graphInactiveState: 'inactiveByLegend', - filterFunctions: { - ...legendDataFilterFunctions.current, - }, + }, + defaultEdge: { + type: 'cubic-horizontal', + // type: 'flow-line', + // type: 'polyline', + // type: 'line', + /* configure the bending radius and min distance to the end nodes */ + style: { + radius: 10, + offset: 30, + endArrow: true, + /* and other styles */ + // stroke: '#F6BD16', }, - }); - - graphRef.current = new G6.TreeGraph({ - container: 'semanticGraph', - width, - height, - linkCenter: true, - modes: { - default: [ - { - type: 'collapse-expand', - onChange: function onChange(item, collapsed) { - const data = item.get('model'); - data.collapsed = collapsed; - return true; - }, - }, - 'drag-node', - 'drag-canvas', - // 'activate-relations', - 'zoom-canvas', - { - type: 'activate-relations', - trigger: 'mouseenter', // 触发方式,可以是 'mouseenter' 或 'click' - resetSelected: true, // 点击空白处时,是否取消高亮 - }, - ], + // style: { + // stroke: '#A3B1BF', + // }, + }, + layout: { + type: 'mindmap', + direction: 'H', + getId: function getId(d) { + return d.id; }, - defaultNode: { - size: 26, - labelCfg: { - position: 'bottom', - style: { - stroke: '#fff', - lineWidth: 4, - }, - }, + getHeight: function getHeight() { + return 16; }, - - layout: { - type: 'dendrogram', - direction: 'LR', - nodeSep: 200, - rankSep: 300, - radial: true, + getWidth: function getWidth() { + return 16; }, - plugins: [legend, tooltip, toolbar], - }); + getVGap: function getVGap() { + return 30; + }, + getHGap: function getHGap() { + return 100; + }, + // type: 'dendrogram', + // direction: 'LR', + // nodeSep: 200, + // rankSep: 300, + // radial: true, + }, + plugins: [legend, tooltip, toolbar], + }); - 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 = graph.findById(shapeGroupId); - // const model = targetNode.getModel(); - toggleNodeVisibility(graph, targetNode, isActive); - toggleChildrenVisibility(graph, targetNode, isActive); - } - }); + // 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 = graph.findById(shapeGroupId); + // const model = targetNode.getModel(); + toggleNodeVisibility(graph, targetNode, isActive); + toggleChildrenVisibility(graph, targetNode, isActive); + } + }); - const graph = graphRef.current; + const graph = graphRef.current; - graph.node(function (node) { - return { - label: node.name, - labelCfg: { style: { fill: '#3c3c3c' } }, - }; - }); + graph.node(function (node) { + return { + label: node.name, + labelCfg: { style: { fill: '#3c3c3c' } }, + }; + }); + console.log(graphData, 'graphData'); + // graph.data(graphData); + graph.changeData(graphData); + graph.render(); + graph.fitView(); - graph.data(graphData); - graph.render(); - graph.fitView(); + setAllActiveLegend(legend); - setAllActiveLegend(legend); + const rootNode = graph.findById('root'); + graph.hideItem(rootNode); + if (typeof window !== 'undefined') + window.onresize = () => { + if (!graph || graph.get('destroyed')) return; + if (!container || !container.scrollWidth || !container.scrollHeight) return; + graph.changeSize(container.scrollWidth, container.scrollHeight); + }; + // } + }, [graphData]); - const rootNode = graph.findById('root'); - graph.hideItem(rootNode); - if (typeof window !== 'undefined') - window.onresize = () => { - if (!graph || graph.get('destroyed')) return; - if (!container || !container.scrollWidth || !container.scrollHeight) return; - graph.changeSize(container.scrollWidth, container.scrollHeight); - }; - } - }, [domainId, graphData]); - - return
    ; + return ( + <> + + + + { + const { value } = e.target; + console.log(value, 'value'); + setGraphShowType(value); + changeGraphData(dataSourceListData, value); + }} + > + 维度 + 指标 + + + +
    + + ); }; export default connect(({ domainManger }: { domainManger: StateType }) => ({ domainManger,