diff --git a/webapp/packages/supersonic-fe/config/routes.ts b/webapp/packages/supersonic-fe/config/routes.ts index 892d34366..e7c62b84e 100644 --- a/webapp/packages/supersonic-fe/config/routes.ts +++ b/webapp/packages/supersonic-fe/config/routes.ts @@ -1,4 +1,4 @@ -export const ROUTE_AUTH_CODES = {}; +export const ROUTE_AUTH_CODES = { SYSTEM_ADMIN: 'SYSTEM_ADMIN' }; const ENV_KEY = { CHAT: 'chat', @@ -88,8 +88,8 @@ const ROUTES = [ { path: '/system', name: 'system', - hideInMenu: true, component: './System', + access: ROUTE_AUTH_CODES.SYSTEM_ADMIN, }, { path: '/', diff --git a/webapp/packages/supersonic-fe/src/app.tsx b/webapp/packages/supersonic-fe/src/app.tsx index 1d42b6325..59e2040c0 100644 --- a/webapp/packages/supersonic-fe/src/app.tsx +++ b/webapp/packages/supersonic-fe/src/app.tsx @@ -11,7 +11,9 @@ import { queryCurrentUser } from './services/user'; import { traverseRoutes, isMobile, getToken } from './utils/utils'; import { publicPath } from '../config/defaultSettings'; import { Copilot } from 'supersonic-chat-sdk'; +import { getSystemConfig } from '@/services/user'; export { request } from './services/request'; +import { ROUTE_AUTH_CODES } from '../config/routes'; const replaceRoute = '/'; @@ -37,8 +39,13 @@ export const initialStateConfig = { ), }; -const getAuthCodes = () => { - return []; +const getAuthCodes = (params: any) => { + const { currentUser, systemConfigAdmins } = params; + const codes = []; + if (Array.isArray(systemConfigAdmins) && systemConfigAdmins.includes(currentUser?.staffName)) { + codes.push(ROUTE_AUTH_CODES.SYSTEM_ADMIN); + } + return codes; }; export async function getInitialState(): Promise<{ @@ -58,6 +65,16 @@ export async function getInitialState(): Promise<{ return undefined; }; + const fetchSystemConfigPermission = async () => { + try { + const { code, data } = await getSystemConfig(); + if (code === 200) { + const { admins } = data; + return [...admins]; + } + } catch (error) {} + return []; + }; let currentUser: any; if (!window.location.pathname.includes('login')) { currentUser = await fetchUserInfo(); @@ -70,7 +87,12 @@ export async function getInitialState(): Promise<{ } } - const authCodes = getAuthCodes(); + const systemConfigAdmins = await fetchSystemConfigPermission(); + + const authCodes = getAuthCodes({ + currentUser, + systemConfigAdmins, + }); return { fetchUserInfo, diff --git a/webapp/packages/supersonic-fe/src/components/RightContent/index.tsx b/webapp/packages/supersonic-fe/src/components/RightContent/index.tsx index 99fed0d32..4856aad55 100644 --- a/webapp/packages/supersonic-fe/src/components/RightContent/index.tsx +++ b/webapp/packages/supersonic-fe/src/components/RightContent/index.tsx @@ -1,51 +1,17 @@ import { Space } from 'antd'; -import React, { useEffect, useState } from 'react'; -import { useModel, history } from 'umi'; +import React from 'react'; + import Avatar from './AvatarDropdown'; -import { SettingOutlined } from '@ant-design/icons'; -import { getSystemConfig } from '@/services/user'; import styles from './index.less'; export type SiderTheme = 'light' | 'dark'; const GlobalHeaderRight: React.FC = () => { - const { initialState } = useModel('@@initialState'); - const [hasSettingPermisson, setHasSettingPermisson] = useState(false); - useEffect(() => { - querySystemConfig(); - }, []); - - if (!initialState || !initialState.settings) { - return null; - } - const { currentUser = {} } = initialState as any; - - const querySystemConfig = async () => { - const { code, data } = await getSystemConfig(); - if (code === 200) { - const { admins } = data; - if (Array.isArray(admins) && admins.includes(currentUser?.staffName)) { - setHasSettingPermisson(true); - } - } - }; - function handleLogin() {} return ( - {hasSettingPermisson && ( - { - history.push(`/system`); - }} - > - - - )} ); }; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/Market.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/Market.tsx index cb99b91d1..f43952892 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/Market.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/Market.tsx @@ -190,9 +190,11 @@ const ClassMetricTable: React.FC = ({ domainManger, dispatch }) => { if (record.hasAdminRes) { return ( { - history.replace(`/model/${record.domainId}/${record.modelId}/metric`); - }} + target="blank" + href={`/webapp/model/${record.domainId}/${record.modelId}/metric`} + // onClick={() => { + // history.push(`/model/${record.domainId}/${record.modelId}/metric`); + // }} > {record.modelName} diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricTrendSection.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricTrendSection.tsx index 3fb507977..a184c63fe 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricTrendSection.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricTrendSection.tsx @@ -22,6 +22,7 @@ import MetricTable from './Table'; import { ColumnConfig } from '../data'; import dayjs from 'dayjs'; import { ISemantic } from '../../data'; +import { DateFieldMap } from '@/pages/SemanticModel/constant'; const FormItem = Form.Item; const { Option } = Select; @@ -32,11 +33,6 @@ type Props = { }; const MetricTrendSection: React.FC = ({ metircData }) => { - const dateFieldMap = { - [DateRangeType.DAY]: 'sys_imp_date', - [DateRangeType.WEEK]: 'sys_imp_week', - [DateRangeType.MONTH]: 'sys_imp_month', - }; const indicatorFields = useRef<{ name: string; column: string }[]>([]); const [metricTrendData, setMetricTrendData] = useState([]); const [metricTrendLoading, setMetricTrendLoading] = useState(false); @@ -61,7 +57,7 @@ const MetricTrendSection: React.FC = ({ metircData }) => { }>({ startDate: dayjs().subtract(6, 'days').format('YYYY-MM-DD'), endDate: dayjs().format('YYYY-MM-DD'), - dateField: dateFieldMap[DateRangeType.DAY], + dateField: DateFieldMap[DateRangeType.DAY], }); const [rowNumber, setRowNumber] = useState(5); const [chartType, setChartType] = useState<'chart' | 'table'>('chart'); @@ -227,12 +223,12 @@ const MetricTrendSection: React.FC = ({ metircData }) => { onDateRangeChange={(value, config) => { const [startDate, endDate] = value; const { dateSettingType, dynamicParams, staticParams } = config; - let dateField = dateFieldMap[DateRangeType.DAY]; + let dateField = DateFieldMap[DateRangeType.DAY]; if (DateSettingType.DYNAMIC === dateSettingType) { - dateField = dateFieldMap[dynamicParams.dateRangeType]; + dateField = DateFieldMap[dynamicParams.dateRangeType]; } if (DateSettingType.STATIC === dateSettingType) { - dateField = dateFieldMap[staticParams.dateRangeType]; + dateField = DateFieldMap[staticParams.dateRangeType]; } setPeriodDate({ startDate, endDate, dateField }); }} 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 56ecc80d9..fb10d69e9 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx @@ -1029,6 +1029,7 @@ const DomainManger: React.FC = ({ domainManger, dispatch }) => { {createDimensionModalVisible && ( = ({ - domainManger, - // graphShowType = SemanticNodeType.DIMENSION, - // graphShowType, - dispatch, -}) => { - const ref = useRef(null); - const dataSourceRef = useRef([]); - const [graphData, setGraphData] = useState(); - const [createDimensionModalVisible, setCreateDimensionModalVisible] = useState(false); - const [createMetricModalVisible, setCreateMetricModalVisible] = useState(false); - const [infoDrawerVisible, setInfoDrawerVisible] = useState(false); - - const [currentNodeData, setCurrentNodeData] = useState(); - - const legendDataRef = useRef([]); - const graphRef = useRef(null); - // const legendDataFilterFunctions = useRef({}); - const [dimensionItem, setDimensionItem] = useState(); - - const [metricItem, setMetricItem] = useState(); - - const [nodeDataSource, setNodeDataSource] = useState(); - - const [dataSourceInfoList, setDataSourceInfoList] = useState([]); - - const { dimensionList, metricList, selectModelId: modelId, selectDomainId } = domainManger; - - const dimensionListRef = useRef([]); - const metricListRef = useRef([]); - - const [confirmModalOpenState, setConfirmModalOpenState] = useState(false); - const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false); - - 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 handleSeachNode = (text: string) => { - const filterData = dataSourceRef.current.reduce( - (data: ISemantic.IDomainSchemaRelaList, item: ISemantic.IDomainSchemaRelaItem) => { - const { dimensions, metrics } = item; - const dimensionsList = dimensions.filter((dimension) => { - return dimension.name.includes(text); - }); - const metricsList = metrics.filter((metric) => { - return metric.name.includes(text); - }); - data.push({ - ...item, - dimensions: dimensionsList, - metrics: metricsList, - }); - return data; - }, - [], - ); - const rootGraphData = changeGraphData(filterData); - refreshGraphData(rootGraphData); - }; - - 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, - label: name, - order: 4, - ...typeConfigs.datasource, - }; - }); - legendDataRef.current = legendList as any; - }; - - const queryDataSourceList = async (params: { - domainId: number; - graphShowType?: SemanticNodeType; - }) => { - const { code, data } = await getDomainSchemaRela(params.domainId); - if (code === 200) { - if (data) { - setDataSourceInfoList( - data.map((item: ISemantic.IDomainSchemaRelaItem) => { - return item.model; - }), - ); - const graphRootData = changeGraphData(data); - dataSourceRef.current = data; - initLegendData(graphRootData); - setGraphData(graphRootData); - return graphRootData; - } - return false; - } else { - return false; - } - }; - - useEffect(() => { - graphLegendDataSourceIds.current = undefined; - graphRef.current = null; - queryDataSourceList({ domainId: selectDomainId }); - }, [selectDomainId]); - - // 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 handleContextMenuClickEdit = (item: IItemBaseConfig) => { - const targetData = item.model; - if (!targetData) { - return; - } - const datasource = loopNodeFindDataSource(item); - if (datasource) { - setNodeDataSource({ - ...datasource, - id: datasource.uid, - }); - } - if (targetData.nodeType === SemanticNodeType.DATASOURCE) { - setCreateDataSourceModalOpen(true); - return; - } - if (targetData.nodeType === SemanticNodeType.DIMENSION) { - const targetItem = dimensionListRef.current.find((item) => item.id === targetData.uid); - if (targetItem) { - setDimensionItem({ ...targetItem }); - setCreateDimensionModalVisible(true); - } else { - message.error('获取维度初始化数据失败'); - } - return; - } - if (targetData.nodeType === SemanticNodeType.METRIC) { - const targetItem = metricListRef.current.find((item) => item.id === targetData.uid); - if (targetItem) { - setMetricItem({ ...targetItem }); - setCreateMetricModalVisible(true); - } else { - message.error('获取指标初始化数据失败'); - } - return; - } - }; - - const handleContextMenuClickCreate = (item: IItemBaseConfig, key: string) => { - const datasource = item.model; - if (!datasource) { - return; - } - setNodeDataSource({ - ...datasource, - id: datasource.uid, - }); - if (key === 'createDimension') { - setCreateDimensionModalVisible(true); - } - if (key === 'createMetric') { - setCreateMetricModalVisible(true); - } - setDimensionItem(undefined); - setMetricItem(undefined); - }; - - const handleContextMenuClickDelete = (item: IItemBaseConfig) => { - const targetData = item.model; - if (!targetData) { - return; - } - if (targetData.nodeType === SemanticNodeType.DATASOURCE) { - setCurrentNodeData({ - ...targetData, - id: targetData.uid, - }); - setConfirmModalOpenState(true); - return; - } - if (targetData.nodeType === SemanticNodeType.DIMENSION) { - const targetItem = dimensionListRef.current.find((item) => item.id === targetData.uid); - if (targetItem) { - setCurrentNodeData({ ...targetData, ...targetItem }); - setConfirmModalOpenState(true); - } else { - message.error('获取维度初始化数据失败'); - } - } - if (targetData.nodeType === SemanticNodeType.METRIC) { - const targetItem = metricListRef.current.find((item) => item.id === targetData.uid); - if (targetItem) { - setCurrentNodeData({ ...targetData, ...targetItem }); - setConfirmModalOpenState(true); - } else { - message.error('获取指标初始化数据失败'); - } - } - }; - - const handleContextMenuClick = (key: string, item: Item) => { - if (!item?._cfg) { - return; - } - switch (key) { - case 'edit': - case 'editDatasource': - handleContextMenuClickEdit(item._cfg); - break; - case 'delete': - case 'deleteDatasource': - handleContextMenuClickDelete(item._cfg); - break; - case 'createDimension': - case 'createMetric': - handleContextMenuClickCreate(item._cfg, key); - break; - default: - break; - } - }; - - const handleNodeTypeClick = (nodeData: any) => { - setCurrentNodeData(nodeData); - setInfoDrawerVisible(true); - }; - - const graphConfigMap = { - dendrogram: { - defaultEdge: { - type: 'cubic-horizontal', - }, - layout: { - type: 'dendrogram', - direction: 'LR', - animate: false, - nodeSep: 200, - rankSep: 300, - radial: true, - }, - }, - mindmap: { - defaultEdge: { - type: 'polyline', - }, - layout: { - type: 'mindmap', - animate: false, - direction: 'H', - getHeight: () => { - return 50; - }, - getWidth: () => { - return 50; - }, - getVGap: () => { - return 10; - }, - getHGap: () => { - return 50; - }, - }, - }, - }; - - function handleToolBarClick(code: string) { - if (code === 'visibleMode') { - visibleModeOpenRef.current = !visibleModeOpenRef.current; - setVisibleModeOpen(visibleModeOpenRef.current); - return; - } - visibleModeOpenRef.current = false; - setVisibleModeOpen(false); - } - - const lessNodeZoomRealAndMoveCenter = () => { - const bbox = graphRef.current.get('group').getBBox(); - - // 计算图形的中心点 - const centerX = (bbox.minX + bbox.maxX) / 2; - const centerY = (bbox.minY + bbox.maxY) / 2; - - // 获取画布的中心点 - const canvasWidth = graphRef.current.get('width'); - const canvasHeight = graphRef.current.get('height'); - const canvasCenterX = canvasWidth / 2; - const canvasCenterY = canvasHeight / 2; - - // 计算画布需要移动的距离 - const dx = canvasCenterX - centerX; - const dy = canvasCenterY - centerY; - - // 将画布移动到中心点 - graphRef.current.translate(dx, dy); - - // 将缩放比例设置为 1,以画布中心点为中心进行缩放 - graphRef.current.zoomTo(1, { x: canvasCenterX, y: canvasCenterY }); - }; - - useEffect(() => { - if (!Array.isArray(graphData?.children)) { - return; - } - const container = document.getElementById('semanticGraph'); - const width = container!.scrollWidth; - const height = container!.scrollHeight || 500; - - const graph = graphRef.current; - - if (!graph && graphData) { - const graphNodeList = flatGraphDataNode(graphData.children); - const graphConfigKey = graphNodeList.length > 20 ? 'dendrogram' : 'mindmap'; - - // getLegendDataFilterFunctions(); - const toolbar = initToolBar({ onSearch: handleSeachNode, onClick: handleToolBarClick }); - const tooltip = initTooltips(); - const contextMenu = initContextMenu({ - onMenuClick: handleContextMenuClick, - }); - // const legend = initLegend({ - // nodeData: legendDataRef.current, - // filterFunctions: { ...legendDataFilterFunctions.current }, - // }); - - graphRef.current = new G6.Graph({ - container: 'semanticGraph', - width, - height, - // translate the graph to align the canvas's center, support by v3.5.1 - fitCenter: 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', - { - type: 'zoom-canvas', - sensitivity: 0.3, // 设置缩放灵敏度,值越小,缩放越不敏感,默认值为 1 - }, - { - type: 'activate-relations', - trigger: 'mouseenter', // 触发方式,可以是 'mouseenter' 或 'click' - resetSelected: true, // 点击空白处时,是否取消高亮 - }, - ], - }, - defaultNode: { - type: 'card-node', - }, - }); - - // graphRef.current = new G6.TreeGraph({ - // container: 'semanticGraph', - // width, - // height, - // 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', - // { - // type: 'zoom-canvas', - // sensitivity: 0.3, // 设置缩放灵敏度,值越小,缩放越不敏感,默认值为 1 - // }, - // { - // type: 'activate-relations', - // trigger: 'mouseenter', // 触发方式,可以是 'mouseenter' 或 'click' - // resetSelected: true, // 点击空白处时,是否取消高亮 - // }, - // ], - // }, - // defaultNode: { - // type: 'card-node', - // }, - // defaultEdge: { - // type: 'cubic-horizontal', - // style: { - // stroke: '#CED4D9', - // }, - // }, - // // defaultNode: { - // // size: 26, - // // anchorPoints: [ - // // [0, 0.5], - // // [1, 0.5], - // // ], - // // labelCfg: { - // // position: 'right', - // // offset: 5, - // // style: { - // // stroke: '#fff', - // // lineWidth: 4, - // // }, - // // }, - // // }, - // // defaultEdge: { - // // type: graphConfigMap[graphConfigKey].defaultEdge.type, - // // }, - // layout: { - // ...graphConfigMap[graphConfigKey].layout, - // }, - // plugins: [tooltip, toolbar, contextMenu], - // // plugins: [legend, tooltip, toolbar, contextMenu], - // }); - - cardNodeRegister(graphRef.current); - - graphRef.current.set('initGraphData', graphData); - graphRef.current.set('initDataSource', dataSourceRef.current); - - // const legendCanvas = legend._cfgs.legendCanvas; - - // legend模式事件方法bindEvents会有点击图例空白清空选中的逻辑,在注册click事件前,先将click事件队列清空; - // 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); - // }); - - graphRef.current.node(function (node: NodeConfig) { - return getNodeConfigByType(node, { - label: node.name, - }); - }); - - const data = { - nodes: [ - { - name: 'cardNodeApp', - ip: '127.0.0.1', - nodeError: true, - dataType: 'root', - keyInfo: 'this is a card node info', - x: 100, - y: 50, - }, - { - name: 'cardNodeApp', - ip: '127.0.0.1', - nodeError: false, - dataType: 'subRoot', - keyInfo: 'this is sub root', - x: 100, - y: 150, - }, - { - name: 'cardNodeApp', - ip: '127.0.0.1', - nodeError: false, - dataType: 'subRoot', - keyInfo: 'this is sub root', - x: 100, - y: 250, - children: [ - { - name: 'sub', - }, - ], - }, - ], - edges: [], - }; - - graphRef.current.data(data); - // graphRef.current.data(graphData); - graphRef.current.render(); - - const nodeCount = graphRef.current.getNodes().length; - if (nodeCount < 10) { - lessNodeZoomRealAndMoveCenter(); - } else { - graphRef.current.fitView([80, 80]); - } - - graphRef.current.on('node:click', (evt: any) => { - const item = evt.item; // 被操作的节点 item - const itemData = item?._cfg?.model; - if (itemData) { - const { nodeType } = itemData; - if ( - [ - SemanticNodeType.DIMENSION, - SemanticNodeType.METRIC, - SemanticNodeType.DATASOURCE, - ].includes(nodeType) - ) { - handleNodeTypeClick(itemData); - return; - } - } - }); - - graphRef.current.on('canvas:click', () => { - setInfoDrawerVisible(false); - }); - - const rootNode = graphRef.current.findById('root'); - graphRef.current.hideItem(rootNode); - if (typeof window !== 'undefined') - window.onresize = () => { - if (!graphRef.current || graphRef.current.get('destroyed')) return; - if (!container || !container.scrollWidth || !container.scrollHeight) return; - graphRef.current.changeSize(container.scrollWidth, container.scrollHeight); - }; - } - }, [graphData]); - - const updateGraphData = async (params?: { graphShowType?: SemanticNodeType }) => { - const graphRootData = await queryDataSourceList({ - modelId, - graphShowType: params?.graphShowType, - }); - if (graphRootData) { - refreshGraphData(graphRootData); - } - }; - - const refreshGraphData = (graphRootData: TreeGraphData) => { - graphRef.current.changeData(graphRootData); - const rootNode = graphRef.current.findById('root'); - graphRef.current.hideItem(rootNode); - graphRef.current.fitView(); - }; - - 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); - if (eventName === 'createDatabase') { - setCreateDataSourceModalOpen(true); - } - if (eventName === 'createDimension') { - setCreateDimensionModalVisible(true); - setDimensionItem(undefined); - } - if (eventName === 'createMetric') { - setCreateMetricModalVisible(true); - setMetricItem(undefined); - } - }} - /> -
- { - setInfoDrawerVisible(false); - }} - open={infoDrawerVisible} - mask={false} - getContainer={false} - onEditBtnClick={(nodeData: any) => { - handleContextMenuClickEdit({ model: nodeData }); - setInfoDrawerVisible(false); - }} - onNodeChange={({ eventName }: { eventName: string }) => { - updateGraphData(); - setInfoDrawerVisible(false); - if (eventName === SemanticNodeType.METRIC) { - dispatch({ - type: 'domainManger/queryMetricList', - payload: { - modelId, - }, - }); - } - if (eventName === SemanticNodeType.DIMENSION) { - dispatch({ - type: 'domainManger/queryDimensionList', - payload: { - modelId, - }, - }); - } - }} - /> - - {createDimensionModalVisible && ( - { - setCreateDimensionModalVisible(false); - updateGraphData(); - dispatch({ - type: 'domainManger/queryDimensionList', - payload: { - modelId, - }, - }); - }} - onCancel={() => { - setCreateDimensionModalVisible(false); - }} - /> - )} - {createMetricModalVisible && ( - { - setCreateMetricModalVisible(false); - updateGraphData(); - dispatch({ - type: 'domainManger/queryMetricList', - payload: { - modelId, - }, - }); - }} - onCancel={() => { - setCreateMetricModalVisible(false); - }} - /> - )} - { - { - setNodeDataSource(undefined); - setCreateDataSourceModalOpen(false); - }} - dataSourceItem={nodeDataSource} - onSubmit={() => { - updateGraphData(); - }} - /> - } - { - { - setConfirmModalOpenState(false); - updateGraphData(); - graphShowTypeState === SemanticNodeType.DIMENSION - ? dispatch({ - type: 'domainManger/queryDimensionList', - payload: { - modelId, - }, - }) - : dispatch({ - type: 'domainManger/queryMetricList', - payload: { - modelId, - }, - }); - }} - onCancelClick={() => { - setConfirmModalOpenState(false); - }} - nodeData={currentNodeData} - /> - } - - ); -}; -export default connect(({ domainManger }: { domainManger: StateType }) => ({ - domainManger, -}))(DomainManger); 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 3fe8c30e1..416813885 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx @@ -423,6 +423,7 @@ const ClassDimensionTable: React.FC = ({ domainManger, dispatch }) => { {createModalVisible && ( void; + bindModalVisible: boolean; + dimensionList: ISemantic.IDimensionItem[]; + onSubmit: (values?: any) => void; +}; + +const FormItem = Form.Item; + +const { TextArea } = Input; + +const CommonDimensionInfoModal: React.FC = ({ + domainId, + onCancel, + bindModalVisible, + dimensionItem, + onSubmit: handleUpdate, +}) => { + const isEdit = !!dimensionItem?.id; + const [form] = Form.useForm(); + const { setFieldsValue, resetFields } = form; + const [saveLoading, setSaveLoading] = useState(false); + const [dimensionOptions, setDimensionOptions] = useState([]); + + useEffect(() => { + queryDimensionList(); + }, []); + + const queryDimensionList = async () => { + setSaveLoading(true); + const { code, data, msg } = await getDimensionList({ domainId }); + setSaveLoading(false); + const dimensionList = data?.list; + if (code === 200 && Array.isArray(dimensionList)) { + const dimensionMap = dimensionList.reduce( + ( + dataMap: Record< + string, + { label: string; options: { label: string; value: number; disabled?: boolean }[] } + >, + item: ISemantic.IDimensionItem, + ) => { + const { modelId, modelName, name, id, commonDimensionId } = item; + const target = dataMap[modelId]; + if (target) { + target.options.push({ + label: name, + value: id, + disabled: !!commonDimensionId, + }); + } else { + dataMap[modelId] = { + label: modelName || `${modelId}`, + options: [ + { + label: name, + value: id, + disabled: !!commonDimensionId, + }, + ], + }; + } + return dataMap; + }, + {}, + ); + setDimensionOptions(Object.values(dimensionMap)); + } else { + message.error(msg); + } + }; + + const handleSubmit = async (isSilenceSubmit = false) => { + const fieldsValue = await form.validateFields(); + await saveDimension( + { + ...fieldsValue, + }, + isSilenceSubmit, + ); + }; + + const saveDimension = async (fieldsValue: any, isSilenceSubmit = false) => { + const queryParams = { + domainId: isEdit ? dimensionItem.domainId : domainId, + type: 'categorical', + ...fieldsValue, + }; + + const { code, msg } = await saveCommonDimension(queryParams); + if (code === 200) { + if (!isSilenceSubmit) { + message.success('编辑公共维度成功'); + handleUpdate(fieldsValue); + } + return; + } + message.error(msg); + }; + + useEffect(() => { + if (dimensionItem) { + setFieldsValue({ ...dimensionItem }); + } else { + resetFields(); + } + }, [dimensionItem]); + + const renderFooter = () => { + return ( + <> + + + + ); + }; + + const renderContent = () => { + return ( + <> + + + + + + + + + } + name="dimensionIds" + // rules={[{ required: true, message: '请选择所要关联的维度' }]} + > +