[improvement][headless-fe] Revised the interaction for semantic modeling routing and implemented the initial version of metric management switching.

This commit is contained in:
tristanliu
2024-11-26 20:28:24 +08:00
parent 0edadd01eb
commit 2eca2d1c14
23 changed files with 571 additions and 895 deletions

View File

@@ -62,27 +62,85 @@ const ROUTES = [
envEnableList: [ENV_KEY.SEMANTIC], envEnableList: [ENV_KEY.SEMANTIC],
routes: [ routes: [
{ {
path: '/model/:domainId', path: '/model/',
component: './SemanticModel/DomainManager', redirect: '/model/domain',
envEnableList: [ENV_KEY.SEMANTIC], },
{
path: '/model/domain/',
component: './SemanticModel/OverviewContainer',
routes: [ routes: [
{ {
path: '/model/:domainId/:menuKey', path: '/model/domain/:domainId',
component: './SemanticModel/DomainManager', component: './SemanticModel/DomainManager',
routes: [
{
path: '/model/domain/:domainId/:menuKey',
component: './SemanticModel/DomainManager',
},
],
},
{
path: '/model/domain/manager/:domainId/:modelId',
component: './SemanticModel/ModelManager',
routes: [
{
path: '/model/domain/manager/:domainId/:modelId/:menuKey',
component: './SemanticModel/ModelManager',
},
],
}, },
], ],
}, },
{ {
path: '/model/manager/:domainId/:modelId', path: '/model/metric/:domainId/:modelId/:metricId',
component: './SemanticModel/ModelManager', component: './SemanticModel/Metric/Edit',
envEnableList: [ENV_KEY.SEMANTIC], envEnableList: [ENV_KEY.SEMANTIC],
routes: [ // routes: [
{ // {
path: '/model/manager/:domainId/:modelId/:menuKey', // path: '/model/manager/:domainId/:modelId/:menuKey',
component: './SemanticModel/ModelManager', // component: './SemanticModel/ModelManager',
}, // },
], // ],
}, },
// {
// path: '/model/manager/',
// component: './SemanticModel/OverviewContainer',
// routes: [
// {
// path: '/model/manager/:domainId/:modelId',
// component: './SemanticModel/ModelManager',
// routes: [
// {
// path: '/model/manager/:domainId/:modelId/:menuKey',
// component: './SemanticModel/ModelManager',
// },
// ],
// },
// ],
// },
// {
// path: '/model/:domainId',
// component: './SemanticModel/DomainManager',
// envEnableList: [ENV_KEY.SEMANTIC],
// routes: [
// {
// path: '/model/:domainId/:menuKey',
// component: './SemanticModel/DomainManager',
// },
// ],
// },
// {
// path: '/model/manager/:domainId/:modelId',
// component: './SemanticModel/ModelManager',
// envEnableList: [ENV_KEY.SEMANTIC],
// routes: [
// {
// path: '/model/manager/:domainId/:modelId/:menuKey',
// component: './SemanticModel/ModelManager',
// },
// ],
// },
// { // {
// path: '/model/:domainId/:modelId/:menuKey', // path: '/model/:domainId/:modelId/:menuKey',
// component: './SemanticModel/DomainManager', // component: './SemanticModel/DomainManager',

View File

@@ -1,103 +1,26 @@
import { message } from 'antd'; import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { history, useParams, useModel } from '@umijs/max'; import { history, useParams, useModel } from '@umijs/max';
import { ISemantic } from './data';
import { getDomainList, getDataSetList } from './service';
import DomainManagerTab from './components/DomainManagerTab'; import DomainManagerTab from './components/DomainManagerTab';
import { isArrayOfValues } from '@/utils/utils';
type Props = {}; type Props = {};
const DomainManager: React.FC<Props> = ({}) => { const DomainManager: React.FC<Props> = ({}) => {
const defaultTabKey = 'overview'; const defaultTabKey = 'overview';
const params: any = useParams(); const params: any = useParams();
const domainId = params.domainId;
const domainModel = useModel('SemanticModel.domainData'); const domainModel = useModel('SemanticModel.domainData');
const modelModel = useModel('SemanticModel.modelData');
const databaseModel = useModel('SemanticModel.databaseData'); const { selectDomainId } = domainModel;
const { selectDomainId, domainList, setSelectDomain, setDomainList } = domainModel;
const { selectModelId } = modelModel;
const { MrefreshDatabaseList } = databaseModel;
const menuKey = params.menuKey ? params.menuKey : defaultTabKey; const menuKey = params.menuKey ? params.menuKey : defaultTabKey;
const [collapsedState, setCollapsedState] = useState(true);
const [activeKey, setActiveKey] = useState<string>(menuKey); const [activeKey, setActiveKey] = useState<string>(menuKey);
const [dataSetList, setDataSetList] = useState<ISemantic.IDatasetItem[]>([]);
// const initSelectedDomain = (domainList: ISemantic.IDomainItem[]) => {
// const targetNode = domainList.filter((item: any) => {
// return `${item.id}` === domainId;
// })[0];
// if (!targetNode) {
// const firstRootNode = domainList.filter((item: any) => {
// return item.parentId === 0;
// })[0];
// if (firstRootNode) {
// const { id } = firstRootNode;
// setSelectDomain(firstRootNode);
// setActiveKey(menuKey);
// pushUrlMenu(id, 0, menuKey);
// }
// } else {
// setSelectDomain(targetNode);
// }
// };
// const initProjectTree = async () => {
// const { code, data, msg } = await getDomainList();
// if (code === 200) {
// initSelectedDomain(data);
// setDomainList(data);
// } else {
// message.error(msg);
// }
// };
// useEffect(() => {
// initProjectTree();
// MrefreshDatabaseList();
// }, []);
// useEffect(() => {
// if (!selectDomainId) {
// return;
// }
// // queryModelList();
// queryDataSetList();
// }, [selectDomainId]);
// const queryDataSetList = async () => {
// const { code, data, msg } = await getDataSetList(selectDomainId);
// if (code === 200) {
// setDataSetList(data);
// if (!isArrayOfValues(data)) {
// setActiveKey(defaultTabKey);
// }
// } else {
// message.error(msg);
// }
// };
const pushUrlMenu = (domainId: number, menuKey: string) => { const pushUrlMenu = (domainId: number, menuKey: string) => {
history.push(`/model/${domainId}/${menuKey}`); history.push(`/model/domain/${domainId}/${menuKey}`);
}; };
const cleanModelInfo = (domainId) => {
setActiveKey(defaultTabKey);
pushUrlMenu(domainId, defaultTabKey);
// setSelectModel(undefined);
};
// const handleCollapsedBtn = () => {
// setCollapsedState(!collapsedState);
// };
return ( return (
<DomainManagerTab <DomainManagerTab
activeKey={activeKey} activeKey={activeKey}
dataSetList={dataSetList}
onBackDomainBtnClick={() => {
cleanModelInfo(selectDomainId);
}}
onMenuChange={(menuKey) => { onMenuChange={(menuKey) => {
setActiveKey(menuKey); setActiveKey(menuKey);
pushUrlMenu(selectDomainId, menuKey); pushUrlMenu(selectDomainId, menuKey);

View File

@@ -155,7 +155,7 @@ const ClassMetricTable: React.FC<Props> = ({}) => {
const columnsConfig = ColumnsConfig({ const columnsConfig = ColumnsConfig({
indicatorInfo: { indicatorInfo: {
url: '/tag/detail/', url: '/tag/detail/:indicatorId',
starType: 'tag', starType: 'tag',
}, },
}); });

View File

@@ -1,7 +1,7 @@
import { message } from 'antd'; import { message } from 'antd';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { getMetricData } from '../service'; import { getMetricData } from '../service';
import { useParams } from '@umijs/max'; import { useParams, useModel } from '@umijs/max';
import styles from './style.less'; import styles from './style.less';
import { ISemantic } from '../data'; import { ISemantic } from '../data';
import MetricInfoEditSider from './MetricInfoEditSider'; import MetricInfoEditSider from './MetricInfoEditSider';
@@ -14,7 +14,8 @@ const MetricDetail: React.FC<Props> = () => {
const params: any = useParams(); const params: any = useParams();
const metricId = params.metricId; const metricId = params.metricId;
const [metircData, setMetircData] = useState<ISemantic.IMetricItem>(); const [metircData, setMetircData] = useState<ISemantic.IMetricItem>();
const metricModel = useModel('SemanticModel.metricData');
const { selectMetric, setSelectMetric } = metricModel;
const [settingKey, setSettingKey] = useState<MetricSettingKey>(MetricSettingKey.BASIC); const [settingKey, setSettingKey] = useState<MetricSettingKey>(MetricSettingKey.BASIC);
useEffect(() => { useEffect(() => {
@@ -24,10 +25,17 @@ const MetricDetail: React.FC<Props> = () => {
queryMetricData(metricId); queryMetricData(metricId);
}, [metricId]); }, [metricId]);
useEffect(() => {
return () => {
setSelectMetric(undefined);
};
}, []);
const queryMetricData = async (metricId: string) => { const queryMetricData = async (metricId: string) => {
const { code, data, msg } = await getMetricData(metricId); const { code, data, msg } = await getMetricData(metricId);
if (code === 200) { if (code === 200) {
setMetircData({ ...data }); setMetircData({ ...data });
setSelectMetric({ ...data });
return; return;
} }
message.error(msg); message.error(msg);

View File

@@ -905,7 +905,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
key="console" key="console"
onClick={() => { onClick={() => {
history.replace( history.replace(
`/model/manager/${domainId}/${modelId || metricItem?.modelId}/dataSource`, `/model/domain/manager/${domainId}/${modelId || metricItem?.modelId}/dataSource`,
); );
onCancel?.(); onCancel?.();
}} }}

View File

@@ -4,27 +4,18 @@ import ModelManagerTab from './components/ModelManagerTab';
type Props = {}; type Props = {};
const OverviewContainer: React.FC<Props> = ({}) => { const ModelManager: React.FC<Props> = ({}) => {
const defaultTabKey = 'overview'; const defaultTabKey = 'overview';
const params: any = useParams(); const params: any = useParams();
const domainId = params.domainId;
const modelId = params.modelId; const modelId = params.modelId;
const domainModel = useModel('SemanticModel.domainData'); const domainModel = useModel('SemanticModel.domainData');
const modelModel = useModel('SemanticModel.modelData'); const modelModel = useModel('SemanticModel.modelData');
const dimensionModel = useModel('SemanticModel.dimensionData'); const dimensionModel = useModel('SemanticModel.dimensionData');
const metricModel = useModel('SemanticModel.metricData'); const metricModel = useModel('SemanticModel.metricData');
const databaseModel = useModel('SemanticModel.databaseData'); const { selectDomainId } = domainModel;
const { selectDomainId, domainList, setSelectDomain, setDomainList } = domainModel; const { selectModelId, modelList } = modelModel;
const {
selectModelId,
modelList,
MrefreshModelList,
setSelectModel,
setModelTableHistoryParams,
} = modelModel;
const { MrefreshDimensionList } = dimensionModel; const { MrefreshDimensionList } = dimensionModel;
const { MrefreshMetricList } = metricModel; const { MrefreshMetricList } = metricModel;
const { MrefreshDatabaseList } = databaseModel;
const menuKey = params.menuKey ? params.menuKey : !Number(modelId) ? defaultTabKey : ''; const menuKey = params.menuKey ? params.menuKey : !Number(modelId) ? defaultTabKey : '';
const [activeKey, setActiveKey] = useState<string>(menuKey); const [activeKey, setActiveKey] = useState<string>(menuKey);
@@ -35,7 +26,7 @@ const OverviewContainer: React.FC<Props> = ({}) => {
}; };
useEffect(() => { useEffect(() => {
if (!selectModelId) { if (!selectModelId || `${selectModelId}` === `${modelId}`) {
return; return;
} }
initModelConfig(); initModelConfig();
@@ -44,22 +35,13 @@ const OverviewContainer: React.FC<Props> = ({}) => {
}, [selectModelId]); }, [selectModelId]);
const pushUrlMenu = (domainId: number, modelId: number, menuKey: string) => { const pushUrlMenu = (domainId: number, modelId: number, menuKey: string) => {
history.push(`/model/manager/${domainId}/${modelId}/${menuKey}`); history.push(`/model/domain/manager/${domainId}/${modelId}/${menuKey}`);
};
const cleanModelInfo = (domainId) => {
setActiveKey(defaultTabKey);
pushUrlMenu(domainId, 0, defaultTabKey);
setSelectModel(undefined);
}; };
return ( return (
<ModelManagerTab <ModelManagerTab
activeKey={activeKey} activeKey={activeKey}
modelList={modelList} modelList={modelList}
onBackDomainBtnClick={() => {
cleanModelInfo(selectDomainId);
}}
onMenuChange={(menuKey) => { onMenuChange={(menuKey) => {
setActiveKey(menuKey); setActiveKey(menuKey);
pushUrlMenu(selectDomainId, selectModelId, menuKey); pushUrlMenu(selectDomainId, selectModelId, menuKey);
@@ -68,4 +50,4 @@ const OverviewContainer: React.FC<Props> = ({}) => {
); );
}; };
export default OverviewContainer; export default ModelManager;

View File

@@ -0,0 +1,128 @@
import React, { useEffect, useState } from 'react';
import { history, useParams, useModel, Outlet } from '@umijs/max';
import DomainListTree from './components/DomainList';
import styles from './components/style.less';
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
import { ISemantic } from './data';
type Props = {};
const OverviewContainer: React.FC<Props> = ({}) => {
const defaultTabKey = 'overview';
const params: any = useParams();
const domainId = params.domainId;
const modelId = params.modelId;
const domainModel = useModel('SemanticModel.domainData');
const modelModel = useModel('SemanticModel.modelData');
const databaseModel = useModel('SemanticModel.databaseData');
const { setSelectDomain, setDomainList, selectDomainId } = domainModel;
const { setSelectModel, setModelTableHistoryParams, MrefreshModelList } = modelModel;
const { MrefreshDatabaseList } = databaseModel;
const menuKey = params.menuKey ? params.menuKey : !Number(modelId) ? defaultTabKey : '';
const [collapsedState, setCollapsedState] = useState(true);
useEffect(() => {
if (!selectDomainId || `${domainId}` === `${selectDomainId}`) {
return;
}
pushUrlMenu(selectDomainId, menuKey);
}, [selectDomainId]);
// const initSelectedDomain = (domainList: ISemantic.IDomainItem[]) => {
// const targetNode = domainList.filter((item: any) => {
// return `${item.id}` === domainId;
// })[0];
// if (!targetNode) {
// const firstRootNode = domainList.filter((item: any) => {
// return item.parentId === 0;
// })[0];
// if (firstRootNode) {
// const { id } = firstRootNode;
// setSelectDomain(firstRootNode);
// pushUrlMenu(id, menuKey);
// }
// } else {
// setSelectDomain(targetNode);
// }
// };
// const initProjectTree = async () => {
// const { code, data, msg } = await getDomainList();
// if (code === 200) {
// initSelectedDomain(data);
// setDomainList(data);
// } else {
// message.error(msg);
// }
// };
// useEffect(() => {
// initProjectTree();
// MrefreshDatabaseList();
// return () => {
// setSelectDomain(undefined);
// };
// }, []);
const pushUrlMenu = (domainId: number, menuKey: string) => {
history.push(`/model/domain/${domainId}/${menuKey}`);
};
const cleanModelInfo = (domainId) => {
pushUrlMenu(domainId, defaultTabKey);
setSelectModel(undefined);
};
const handleCollapsedBtn = () => {
setCollapsedState(!collapsedState);
};
useEffect(() => {
if (!selectDomainId) {
return;
}
queryModelList();
}, [selectDomainId]);
const queryModelList = async () => {
await MrefreshModelList(selectDomainId);
};
return (
<div className={styles.projectBody}>
<div className={styles.projectManger}>
<div className={`${styles.sider} ${!collapsedState ? styles.siderCollapsed : ''}`}>
<div className={styles.treeContainer}>
<DomainListTree
onTreeSelected={(domainData: ISemantic.IDomainItem) => {
const { id } = domainData;
cleanModelInfo(id);
setSelectDomain(domainData);
setModelTableHistoryParams({
[id]: {},
});
}}
// onTreeDataUpdate={() => {
// // initProjectTree();
// }}
/>
</div>
<div
className={styles.siderCollapsedButton}
onClick={() => {
handleCollapsedBtn();
}}
>
{collapsedState ? <LeftOutlined /> : <RightOutlined />}
</div>
</div>
<div className={styles.content}>
<Outlet />
</div>
</div>
</div>
);
};
export default OverviewContainer;

View File

@@ -1,215 +0,0 @@
import { message } from 'antd';
import React, { useEffect, useState } from 'react';
import { history, useParams, useModel } from '@umijs/max';
import DomainListTree from './components/DomainList';
import styles from './components/style.less';
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
import { ISemantic } from './data';
import { getDomainList, getDataSetList } from './service';
import DomainManagerTab from './components/DomainManagerTab';
import { isArrayOfValues } from '@/utils/utils';
import OverviewContainerRight from './components/OverviewContainerRight';
type Props = {
mode: 'domain';
};
const OverviewContainer: React.FC<Props> = ({ mode = 'domain' }) => {
const defaultTabKey = 'overview';
const params: any = useParams();
const domainId = params.domainId;
const modelId = params.modelId;
const domainModel = useModel('SemanticModel.domainData');
const modelModel = useModel('SemanticModel.modelData');
const dimensionModel = useModel('SemanticModel.dimensionData');
const metricModel = useModel('SemanticModel.metricData');
const databaseModel = useModel('SemanticModel.databaseData');
const { selectDomainId, domainList, setSelectDomain, setDomainList } = domainModel;
const {
selectModelId,
modelList,
MrefreshModelList,
setSelectModel,
setModelTableHistoryParams,
} = modelModel;
const { MrefreshDimensionList } = dimensionModel;
const { MrefreshMetricList } = metricModel;
const { MrefreshDatabaseList } = databaseModel;
const menuKey = params.menuKey ? params.menuKey : !Number(modelId) ? defaultTabKey : '';
const [isModel, setIsModel] = useState<boolean>(false);
const [collapsedState, setCollapsedState] = useState(true);
const [activeKey, setActiveKey] = useState<string>(menuKey);
const [dataSetList, setDataSetList] = useState<ISemantic.IDatasetItem[]>([]);
const initSelectedDomain = (domainList: ISemantic.IDomainItem[]) => {
const targetNode = domainList.filter((item: any) => {
return `${item.id}` === domainId;
})[0];
if (!targetNode) {
const firstRootNode = domainList.filter((item: any) => {
return item.parentId === 0;
})[0];
if (firstRootNode) {
const { id } = firstRootNode;
setSelectDomain(firstRootNode);
setActiveKey(menuKey);
pushUrlMenu(id, 0, menuKey);
}
} else {
setSelectDomain(targetNode);
}
};
const initProjectTree = async () => {
const { code, data, msg } = await getDomainList();
if (code === 200) {
initSelectedDomain(data);
setDomainList(data);
} else {
message.error(msg);
}
};
useEffect(() => {
initProjectTree();
MrefreshDatabaseList();
return () => {
setSelectDomain(undefined);
setSelectModel(undefined);
};
}, []);
useEffect(() => {
if (!selectDomainId) {
return;
}
queryModelList();
queryDataSetList();
}, [selectDomainId]);
const queryDataSetList = async () => {
const { code, data, msg } = await getDataSetList(selectDomainId);
if (code === 200) {
setDataSetList(data);
if (!isArrayOfValues(data)) {
setActiveKey(defaultTabKey);
}
} else {
message.error(msg);
}
};
const queryModelList = async () => {
await MrefreshModelList(selectDomainId);
};
useEffect(() => {
if (!selectDomainId) {
return;
}
setIsModel(false);
}, [domainList, selectDomainId]);
const initModelConfig = () => {
setIsModel(true);
const currentMenuKey = menuKey === defaultTabKey ? '' : menuKey;
pushUrlMenu(selectDomainId, selectModelId, currentMenuKey);
setActiveKey(currentMenuKey);
};
useEffect(() => {
if (!selectModelId) {
return;
}
initModelConfig();
MrefreshDimensionList({ modelId: selectModelId });
MrefreshMetricList({ modelId: selectModelId });
}, [selectModelId]);
const pushUrlMenu = (domainId: number, modelId: number, menuKey: string) => {
history.push(`/model/${domainId}/${modelId || 0}/${menuKey}`);
};
const handleModelChange = (model?: ISemantic.IModelItem) => {
if (!model) {
return;
}
if (`${model.id}` === `${selectModelId}`) {
initModelConfig();
}
setSelectModel(model);
};
const cleanModelInfo = (domainId) => {
setIsModel(false);
setActiveKey(defaultTabKey);
pushUrlMenu(domainId, 0, defaultTabKey);
setSelectModel(undefined);
};
const handleCollapsedBtn = () => {
setCollapsedState(!collapsedState);
};
return (
<div className={styles.projectBody}>
<div className={styles.projectManger}>
<div className={`${styles.sider} ${!collapsedState ? styles.siderCollapsed : ''}`}>
<div className={styles.treeContainer}>
<DomainListTree
createDomainBtnVisible={mode === 'domain' ? true : false}
onTreeSelected={(domainData: ISemantic.IDomainItem) => {
const { id } = domainData;
cleanModelInfo(id);
setSelectDomain(domainData);
setModelTableHistoryParams({
[id]: {},
});
}}
onTreeDataUpdate={() => {
initProjectTree();
}}
/>
</div>
<div
className={styles.siderCollapsedButton}
onClick={() => {
handleCollapsedBtn();
}}
>
{collapsedState ? <LeftOutlined /> : <RightOutlined />}
</div>
</div>
<div className={styles.content}>
{selectDomainId ? (
<>
<OverviewContainerRight />
<DomainManagerTab
isModel={isModel}
activeKey={activeKey}
modelList={modelList}
dataSetList={dataSetList}
handleModelChange={(model) => {
handleModelChange(model);
MrefreshModelList(selectDomainId);
}}
onBackDomainBtnClick={() => {
cleanModelInfo(selectDomainId);
}}
onMenuChange={(menuKey) => {
setActiveKey(menuKey);
pushUrlMenu(selectDomainId, selectModelId, menuKey);
}}
/>
</>
) : (
<h2 className={styles.mainTip}></h2>
)}
</div>
</div>
</div>
);
};
export default OverviewContainer;

View File

@@ -1,62 +0,0 @@
import { Outlet } from '@umijs/max';
import { Tabs, Breadcrumb, Space, Radio } from 'antd';
import React, { useRef, useEffect, useState } from 'react';
import { history, useModel } from '@umijs/max';
import { HomeOutlined, FundViewOutlined } from '@ant-design/icons';
import styles from './components/style.less';
const OverviewContainerRight: React.FC = () => {
const domainModel = useModel('SemanticModel.domainData');
const modelModel = useModel('SemanticModel.modelData');
const { selectDomainId, selectDomainName, selectDomain: domainData } = domainModel;
const { selectModelId, selectModelName, setSelectModel } = modelModel;
return (
<>
<Breadcrumb
className={styles.breadcrumb}
separator=""
items={[
{
title: (
<Space
onClick={() => {
// onBackDomainBtnClick?.();
setSelectModel(undefined);
history.push(`/model/${selectDomainId}/overview`);
}}
style={
selectModelName ? { cursor: 'pointer' } : { color: '#296df3', fontWeight: 'bold' }
}
>
<HomeOutlined />
<span>{selectDomainName}</span>
</Space>
),
},
{
type: 'separator',
separator: selectModelName ? '/' : '',
},
{
title: selectModelName ? (
<Space
onClick={() => {
history.push(`/model/manager/${selectDomainId}/${selectModelId}/`);
}}
style={{ color: '#296df3' }}
>
<FundViewOutlined style={{ position: 'relative', top: '2px' }} />
<span>{selectModelName}</span>
</Space>
) : undefined,
},
]}
/>
<Outlet />
</>
);
};
export default OverviewContainerRight;

View File

@@ -0,0 +1,78 @@
import { Outlet } from '@umijs/max';
import { Tabs, Breadcrumb, Space, Radio } from 'antd';
import React, { useRef, useEffect, useState } from 'react';
import { history, useModel } from '@umijs/max';
import { HomeOutlined, FundViewOutlined } from '@ant-design/icons';
import styles from './components/style.less';
const PageBreadcrumb: React.FC = () => {
const domainModel = useModel('SemanticModel.domainData');
const modelModel = useModel('SemanticModel.modelData');
const metricModel = useModel('SemanticModel.metricData');
const { selectDomainId, selectDomainName, selectDomain: domainData } = domainModel;
const { selectModelId, selectModelName, setSelectModel } = modelModel;
const { selectMetric, setSelectMetric } = metricModel;
const items = [
{
title: (
<Space
onClick={() => {
setSelectModel(undefined);
history.push(`/model/domain/${selectDomainId}/overview`);
}}
>
<HomeOutlined />
<span>{selectDomainName}</span>
</Space>
),
},
];
if (selectModelName) {
items.push(
{
type: 'separator',
separator: '/',
},
{
title: (
<Space
onClick={() => {
setSelectMetric(undefined);
history.push(`/model/domain/manager/${selectDomainId}/${selectModelId}/`);
}}
>
<FundViewOutlined style={{ position: 'relative', top: '2px' }} />
<span>{selectModelName}</span>
</Space>
),
},
);
}
if (selectMetric?.name) {
items.push(
{
type: 'separator',
separator: '/',
},
{
title: selectMetric?.name ? (
<Space>
<FundViewOutlined style={{ position: 'relative', top: '2px' }} />
<span>{selectMetric.name}</span>
</Space>
) : undefined,
},
);
}
return (
<>
<Breadcrumb className={styles.breadcrumb} separator="" items={items} />
</>
);
};
export default PageBreadcrumb;

View File

@@ -45,11 +45,10 @@ const DataSetTable: React.FC<Props> = ({ disabledEdit = false }) => {
const [viewList, setViewList] = useState<ISemantic.IDatasetItem[]>(); const [viewList, setViewList] = useState<ISemantic.IDatasetItem[]>();
// useEffect(() => {
// setViewList(dataSetList);
// }, [dataSetList]);
useEffect(() => { useEffect(() => {
if (!selectDomainId) {
return;
}
queryDataSetList(); queryDataSetList();
queryDomainAllModel(); queryDomainAllModel();
}, [selectDomainId]); }, [selectDomainId]);

View File

@@ -3,7 +3,7 @@ import { ProTable } from '@ant-design/pro-components';
import { message, Button, Space, Popconfirm, Input, Select, Tag } from 'antd'; import { message, Button, Space, Popconfirm, Input, Select, Tag } from 'antd';
import React, { useRef, useState, useEffect } from 'react'; import React, { useRef, useState, useEffect } from 'react';
import { StatusEnum, SemanticNodeType } from '../enum'; import { StatusEnum, SemanticNodeType } from '../enum';
import { useModel } from '@umijs/max'; import { useModel, history } from '@umijs/max';
import { SENSITIVE_LEVEL_ENUM, SENSITIVE_LEVEL_OPTIONS, TAG_DEFINE_TYPE } from '../constant'; import { SENSITIVE_LEVEL_ENUM, SENSITIVE_LEVEL_OPTIONS, TAG_DEFINE_TYPE } from '../constant';
import { import {
queryMetric, queryMetric,
@@ -32,7 +32,7 @@ const ClassMetricTable: React.FC<Props> = ({ onEmptyMetricData }) => {
const metricModel = useModel('SemanticModel.metricData'); const metricModel = useModel('SemanticModel.metricData');
const { selectDomainId } = domainModel; const { selectDomainId } = domainModel;
const { selectModelId: modelId } = modelModel; const { selectModelId: modelId } = modelModel;
const { MrefreshMetricList } = metricModel; const { MrefreshMetricList, selectMetric, setSelectMetric } = metricModel;
const [batchSensitiveLevelOpenState, setBatchSensitiveLevelOpenState] = useState<boolean>(false); const [batchSensitiveLevelOpenState, setBatchSensitiveLevelOpenState] = useState<boolean>(false);
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false); const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const [metricItem, setMetricItem] = useState<ISemantic.IMetricItem>(); const [metricItem, setMetricItem] = useState<ISemantic.IMetricItem>();
@@ -144,7 +144,10 @@ const ClassMetricTable: React.FC<Props> = ({ onEmptyMetricData }) => {
const columnsConfig = ColumnsConfig({ const columnsConfig = ColumnsConfig({
indicatorInfo: { indicatorInfo: {
url: '/model/metric/edit/', url: '/model/metric/:domainId/:modelId/:indicatorId',
onNameClick: (record: ISemantic.IMetricItem) => {
setSelectMetric(record);
},
}, },
}); });
@@ -240,8 +243,9 @@ const ClassMetricTable: React.FC<Props> = ({ onEmptyMetricData }) => {
type="link" type="link"
key="metricEditBtn" key="metricEditBtn"
onClick={() => { onClick={() => {
setMetricItem(record); history.push(`/model/metric/${record.domainId}/${record.modelId}/${record.id}`);
setCreateModalVisible(true); // setMetricItem(record);
// setCreateModalVisible(true);
}} }}
> >

View File

@@ -1,14 +1,11 @@
import { DownOutlined, PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
import { Input, message, Tree, Popconfirm, Tooltip, Row, Col, Button, Menu } from 'antd'; import { Input, message, Popconfirm, Tooltip, Row, Col, Button, Menu } from 'antd';
import type { DataNode } from 'antd/lib/tree'; import type { DataNode } from 'antd/lib/tree';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import type { FC, Key } from 'react'; import type { FC } from 'react';
import { useModel } from '@umijs/max'; import { useModel } from '@umijs/max';
import { createDomain, updateDomain, deleteDomain } from '../service'; import { createDomain, updateDomain, deleteDomain } from '../service';
import { treeParentKeyLists } from '../utils';
import DomainInfoForm from './DomainInfoForm'; import DomainInfoForm from './DomainInfoForm';
import { constructorClassTreeFromList, addPathInTreeData } from '../utils';
import { AppstoreOutlined } from '@ant-design/icons';
import styles from './style.less'; import styles from './style.less';
import { ISemantic } from '../data'; import { ISemantic } from '../data';
@@ -42,20 +39,15 @@ const DomainListTree: FC<DomainListProps> = ({
onTreeSelected, onTreeSelected,
onTreeDataUpdate, onTreeDataUpdate,
}) => { }) => {
const [projectTree, setProjectTree] = useState<DataNode[]>([]);
const [projectInfoModalVisible, setProjectInfoModalVisible] = useState<boolean>(false); const [projectInfoModalVisible, setProjectInfoModalVisible] = useState<boolean>(false);
const [domainInfoParams, setDomainInfoParams] = useState<any>({}); const [domainInfoParams, setDomainInfoParams] = useState<any>({});
const [filterValue, setFliterValue] = useState<string>(''); const [filterValue, setFliterValue] = useState<string>('');
const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
const [classList, setClassList] = useState<ISemantic.IDomainItem[]>([]); const [classList, setClassList] = useState<ISemantic.IDomainItem[]>([]);
const domainModel = useModel('SemanticModel.domainData'); const domainModel = useModel('SemanticModel.domainData');
const { selectDomainId, domainList } = domainModel; const { selectDomainId, domainList } = domainModel;
useEffect(() => { useEffect(() => {
const treeData = addPathInTreeData(constructorClassTreeFromList(domainList));
setProjectTree(treeData);
setClassList(domainList); setClassList(domainList);
setExpandedKeys(treeParentKeyLists(treeData));
}, [domainList]); }, [domainList]);
const onSearch = (value: any) => { const onSearch = (value: any) => {
@@ -67,7 +59,7 @@ const DomainListTree: FC<DomainListProps> = ({
return; return;
} }
const targetNodeData = classList.filter((item: any) => { const targetNodeData = classList.filter((item: any) => {
return item.id === selectedKeys; return `${item.id}` === `${selectedKeys}`;
})[0]; })[0];
onTreeSelected?.(targetNodeData); onTreeSelected?.(targetNodeData);
}; };
@@ -84,20 +76,6 @@ const DomainListTree: FC<DomainListProps> = ({
} }
}; };
// const createDefaultModelSet = async (domainId: number) => {
// const { code, msg } = await createDomain({
// modelType: 'add',
// type: 'normal',
// parentId: domainId,
// name: '默认模型集',
// bizName: `defaultModelSet_${(Math.random() * 1000000).toFixed(0)}`,
// isUnique: 1,
// });
// if (code !== 200) {
// message.error(msg);
// }
// };
const domainSubmit = async (values: any) => { const domainSubmit = async (values: any) => {
if (values.modelType === 'add') { if (values.modelType === 'add') {
const { code, data } = await createDomain(values); const { code, data } = await createDomain(values);
@@ -126,21 +104,19 @@ const DomainListTree: FC<DomainListProps> = ({
const { id, name, path, hasEditPermission, parentId, hasModel } = node as any; const { id, name, path, hasEditPermission, parentId, hasModel } = node as any;
const type = parentId === 0 ? 'top' : 'normal'; const type = parentId === 0 ? 'top' : 'normal';
return ( return (
<div className={styles.projectItem}> <div
<span className={styles.projectItem}
className={styles.projectItemTitle} // onClick={() => {
onClick={() => { // handleSelect(id);
handleSelect(id); // }}
}} >
> <span className={styles.projectItemTitle}>{name}</span>
{name}
</span>
{createDomainBtnVisible && hasEditPermission && ( {createDomainBtnVisible && hasEditPermission && (
<span className={`${styles.operation} ${styles.rowHover} `}> <span className={`${styles.operation} ${styles.rowHover} `}>
{Array.isArray(path) && path.length < 2 && !hasModel && ( {Array.isArray(path) && path.length < 2 && !hasModel && (
<PlusOutlined <PlusOutlined
className={styles.icon} className={styles.icon}
onClick={() => { onClick={(e) => {
setDomainInfoParams({ setDomainInfoParams({
modelType: 'add', modelType: 'add',
type: 'normal', type: 'normal',
@@ -148,19 +124,21 @@ const DomainListTree: FC<DomainListProps> = ({
parentName: name, parentName: name,
}); });
setProjectInfoModalVisible(true); setProjectInfoModalVisible(true);
e.stopPropagation();
}} }}
/> />
)} )}
<EditOutlined <EditOutlined
className={styles.icon} className={styles.icon}
onClick={() => { onClick={(e) => {
setDomainInfoParams({ setDomainInfoParams({
modelType: 'edit', modelType: 'edit',
type, type,
...node, ...node,
}); });
setProjectInfoModalVisible(true); setProjectInfoModalVisible(true);
e.stopPropagation();
}} }}
/> />
<Popconfirm <Popconfirm
@@ -172,7 +150,12 @@ const DomainListTree: FC<DomainListProps> = ({
okText="是" okText="是"
cancelText="否" cancelText="否"
> >
<DeleteOutlined className={styles.icon} /> <DeleteOutlined
className={styles.icon}
onClick={(e) => {
e.stopPropagation();
}}
/>
</Popconfirm> </Popconfirm>
</span> </span>
)} )}
@@ -180,14 +163,14 @@ const DomainListTree: FC<DomainListProps> = ({
); );
}; };
const projectRenderTree = filterValue ? projectTreeFlat(projectTree, filterValue) : projectTree;
const handleExpand = (_expandedKeys: Key[]) => {
setExpandedKeys(_expandedKeys as string[]);
};
const items = domainList const items = domainList
.filter((domain) => domain.parentId === 0) .filter((domain) => {
if (filterValue) {
return domain.parentId === 0 && domain.name.includes(filterValue);
} else {
return domain.parentId === 0;
}
})
.map((domain: ISemantic.IDomainItem) => { .map((domain: ISemantic.IDomainItem) => {
return { return {
key: domain.id, key: domain.id,
@@ -245,6 +228,12 @@ const DomainListTree: FC<DomainListProps> = ({
className={styles.search} className={styles.search}
placeholder="请输入名称搜索" placeholder="请输入名称搜索"
onSearch={onSearch} onSearch={onSearch}
onChange={(e) => {
const value = e.target.value;
if (!value) {
setFliterValue(value);
}
}}
/> />
</Col> </Col>
{createDomainBtnVisible && ( {createDomainBtnVisible && (
@@ -265,25 +254,19 @@ const DomainListTree: FC<DomainListProps> = ({
)} )}
</Row> </Row>
</div> </div>
<Menu {selectDomainId && (
mode="inline" <Menu
defaultSelectedKeys={['231']} mode="inline"
openKeys={stateOpenKeys} defaultSelectedKeys={[`${selectDomainId}`]}
onOpenChange={onOpenChange} openKeys={stateOpenKeys}
style={{ width: 256 }} onOpenChange={onOpenChange}
items={items} style={{ width: 256 }}
/> items={items}
{/* <Tree onClick={(info) => {
expandedKeys={expandedKeys} handleSelect(info.key);
onExpand={handleExpand} }}
className={styles.tree} />
selectedKeys={[selectDomainId]} )}
blockNode={true}
switcherIcon={<DownOutlined />}
defaultExpandAll={true}
treeData={projectRenderTree}
titleRender={titleRender}
/> */}
{projectInfoModalVisible && ( {projectInfoModalVisible && (
<DomainInfoForm <DomainInfoForm
basicInfo={domainInfoParams} basicInfo={domainInfoParams}

View File

@@ -1,45 +1,27 @@
import { Tabs, Breadcrumb, Space, Radio } from 'antd'; import { Tabs, Radio } from 'antd';
import React, { useRef, useEffect, useState } from 'react'; import React, { useRef, useEffect, useState } from 'react';
import { history, useModel } from '@umijs/max'; import { useModel } from '@umijs/max';
import ClassDimensionTable from './ClassDimensionTable';
import ClassMetricTable from './ClassMetricTable';
import PermissionSection from './Permission/PermissionSection'; import PermissionSection from './Permission/PermissionSection';
import TagObjectTable from '../Insights/components/TagObjectTable'; import TagObjectTable from '../Insights/components/TagObjectTable';
import TermTable from '../components/Term/TermTable'; import TermTable from '../components/Term/TermTable';
import OverView from './OverView'; import OverView from './OverView';
import styles from './style.less'; import styles from './style.less';
import { HomeOutlined, FundViewOutlined } from '@ant-design/icons';
import { ISemantic } from '../data';
import SemanticGraphCanvas from '../SemanticGraphCanvas'; import SemanticGraphCanvas from '../SemanticGraphCanvas';
import Dimension from '../Dimension';
import ModelMetric from '../components/ModelMetric';
import View from '../View'; import View from '../View';
type Props = { type Props = {
// isModel: boolean;
activeKey: string; activeKey: string;
// modelList: ISemantic.IModelItem[];
dataSetList: ISemantic.IDatasetItem[];
// handleModelChange: (model?: ISemantic.IModelItem) => void;
onBackDomainBtnClick?: () => void;
onMenuChange?: (menuKey: string) => void; onMenuChange?: (menuKey: string) => void;
}; };
const DomainManagerTab: React.FC<Props> = ({ const DomainManagerTab: React.FC<Props> = ({ activeKey, onMenuChange }) => {
activeKey,
// modelList,
dataSetList,
// handleModelChange,
onBackDomainBtnClick,
onMenuChange,
}) => {
const initState = useRef<boolean>(false); const initState = useRef<boolean>(false);
const defaultTabKey = 'metric'; const defaultTabKey = 'metric';
const domainModel = useModel('SemanticModel.domainData'); const domainModel = useModel('SemanticModel.domainData');
const modelModel = useModel('SemanticModel.modelData'); const modelModel = useModel('SemanticModel.modelData');
const { selectDomainId, selectDomainName, selectDomain: domainData } = domainModel; const { selectDomainId, selectDomain: domainData } = domainModel;
const { selectModelId, modelList, selectModelName } = modelModel; const { selectModelId, modelList } = modelModel;
useEffect(() => { useEffect(() => {
initState.current = false; initState.current = false;

View File

@@ -901,7 +901,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
key="console" key="console"
onClick={() => { onClick={() => {
history.replace( history.replace(
`/model/manager/${domainId}/${modelId || metricItem?.modelId}/dataSource`, `/model/domain/manager/${domainId}/${modelId || metricItem?.modelId}/dataSource`,
); );
onCancel?.(); onCancel?.();
}} }}

View File

@@ -18,28 +18,14 @@ import View from '../View';
type Props = { type Props = {
activeKey: string; activeKey: string;
modelList: ISemantic.IModelItem[]; modelList: ISemantic.IModelItem[];
handleModelChange: (model?: ISemantic.IModelItem) => void;
onBackDomainBtnClick?: () => void;
onMenuChange?: (menuKey: string) => void; onMenuChange?: (menuKey: string) => void;
}; };
const ModelManagerTab: React.FC<Props> = ({ const ModelManagerTab: React.FC<Props> = ({ activeKey, onMenuChange }) => {
activeKey,
modelList,
handleModelChange,
onBackDomainBtnClick,
onMenuChange,
}) => {
const initState = useRef<boolean>(false); const initState = useRef<boolean>(false);
const defaultTabKey = 'metric'; const defaultTabKey = 'metric';
const domainModel = useModel('SemanticModel.domainData');
const modelModel = useModel('SemanticModel.modelData'); const modelModel = useModel('SemanticModel.modelData');
const { selectDomainId, selectDomainName, selectDomain: domainData } = domainModel; const { selectModelId } = modelModel;
const { selectModelId, selectModelName } = modelModel;
useEffect(() => {
console.log(modelList, 'modelList');
}, [modelList]);
useEffect(() => { useEffect(() => {
initState.current = false; initState.current = false;

View File

@@ -106,7 +106,7 @@ const ModelTable: React.FC<Props> = ({ modelList, disabledEdit = false, onModelC
onClick={() => { onClick={() => {
setSelectModel(record); setSelectModel(record);
history.push(`/model/manager/${domainId}/${id}`); history.push(`/model/domain/manager/${domainId}/${id}`);
// onModelChange?.(record); // onModelChange?.(record);
}} }}
> >

View File

@@ -6,32 +6,113 @@ import { history } from '@umijs/max';
import { ISemantic } from '../data'; import { ISemantic } from '../data';
import { isString } from 'lodash'; import { isString } from 'lodash';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { isArrayOfValues } from '@/utils/utils'; import { isArrayOfValues, replaceRouteParams } from '@/utils/utils';
import styles from './style.less'; import styles from './style.less';
import IndicatorStar, { StarType } from '../components/IndicatorStar'; import IndicatorStar, { StarType } from '../components/IndicatorStar';
interface IndicatorInfo {
url?: string;
starType?: StarType;
onNameClick?: (record: ISemantic.IMetricItem) => void | boolean;
}
interface ColumnsConfigParams {
indicatorInfo?: IndicatorInfo;
}
const { Text, Paragraph } = Typography; const { Text, Paragraph } = Typography;
export const ColumnsConfig: any = (params?: { export const ColumnsConfig = (params?: ColumnsConfigParams) => {
indicatorInfo?: { const renderAliasAndClassifications = (
url?: string; alias: string | undefined,
starType?: StarType; classifications: string[] | undefined,
onNameClick?: (record: ISemantic.IMetricItem) => void; ) => (
}; <div style={{ marginTop: 8 }}>
}) => { <Space direction="vertical" size={4}>
{alias && (
<Space size={4} style={{ color: '#5f748d', fontSize: 12, margin: '5px 0 5px 0' }}>
<ReadOutlined />
<div style={{ width: 'max-content' }}>:</div>
<span style={{ marginLeft: 2 }}>
<Space size={[0, 8]} wrap>
{isString(alias) &&
alias.split(',').map((aliasName: string) => (
<Tag
color="#eee"
key={aliasName}
style={{
borderRadius: 44,
maxWidth: 90,
minWidth: 40,
backgroundColor: 'rgba(18, 31, 67, 0.04)',
}}
>
<Text
style={{
maxWidth: 80,
color: 'rgb(95, 116, 141)',
textAlign: 'center',
fontSize: 12,
}}
ellipsis={{ tooltip: aliasName }}
>
{aliasName}
</Text>
</Tag>
))}
</Space>
</span>
</Space>
)}
{isArrayOfValues(classifications) && (
<Space size={4} style={{ color: '#5f748d', fontSize: 12, margin: '5px 0 5px 0' }}>
<TagsOutlined />
<div style={{ width: 'max-content' }}>:</div>
<span style={{ marginLeft: 2 }}>
<Space size={[0, 8]} wrap>
{classifications.map((tag: string) => (
<Tag
color="#eee"
key={tag}
style={{
borderRadius: 44,
maxWidth: 90,
minWidth: 40,
backgroundColor: 'rgba(18, 31, 67, 0.04)',
}}
>
<Text
style={{
maxWidth: 80,
color: 'rgb(95, 116, 141)',
textAlign: 'center',
fontSize: 12,
}}
ellipsis={{ tooltip: tag }}
>
{tag}
</Text>
</Tag>
))}
</Space>
</span>
</Space>
)}
</Space>
</div>
);
return { return {
description: { description: {
render: (_, record: ISemantic.IMetricItem) => { render: (_, record: ISemantic.IMetricItem) => (
const { description } = record; <Paragraph
return ( ellipsis={{ tooltip: record.description, rows: 3 }}
<Paragraph style={{ width: 250, marginBottom: 0 }}
ellipsis={{ tooltip: description, rows: 3 }} >
style={{ width: 250, marginBottom: 0 }} {record.description}
> </Paragraph>
{description} ),
</Paragraph>
);
},
}, },
dimensionInfo: { dimensionInfo: {
render: (_, record: ISemantic.IDimensionItem) => { render: (_, record: ISemantic.IDimensionItem) => {
@@ -46,70 +127,26 @@ export const ColumnsConfig: any = (params?: {
<div style={{ color: '#5f748d', fontSize: 14, marginTop: 5, marginLeft: 0 }}> <div style={{ color: '#5f748d', fontSize: 14, marginTop: 5, marginLeft: 0 }}>
{bizName} {bizName}
</div> </div>
{renderAliasAndClassifications(alias, undefined)}
{alias && (
<div style={{ marginTop: 8 }}>
<Space direction="vertical" size={4}>
{alias && (
<Space
size={4}
style={{ color: '#5f748d', fontSize: 12, margin: '5px 0 5px 0' }}
>
<ReadOutlined />
<div style={{ width: 'max-content' }}>:</div>
<span style={{ marginLeft: 2 }}>
<Space size={[0, 8]} wrap>
{isString(alias) &&
alias.split(',').map((aliasName: string) => {
return (
<Tag
color="#eee"
key={aliasName}
style={{
borderRadius: 44,
maxWidth: 90,
minWidth: 40,
backgroundColor: 'rgba(18, 31, 67, 0.04)',
}}
>
<Text
style={{
maxWidth: 80,
color: 'rgb(95, 116, 141)',
textAlign: 'center',
fontSize: 12,
}}
ellipsis={{ tooltip: aliasName }}
>
{aliasName}
</Text>
</Tag>
);
})}
</Space>
</span>
</Space>
)}
</Space>
</div>
)}
</> </>
); );
}, },
}, },
indicatorInfo: { indicatorInfo: {
render: (_, record: ISemantic.IMetricItem) => { render: (_, record: ISemantic.IMetricItem) => {
const { name, alias, bizName, classifications, id, isCollect } = record; const { name, alias, bizName, classifications, id, isCollect, domainId, modelId } = record;
let url = `/metric/detail/`; let url = `/metric/detail/`;
let starType: StarType = 'metric'; let starType: StarType = 'metric';
if (params) { if (params?.indicatorInfo) {
if (params?.indicatorInfo?.url) { url = replaceRouteParams(params.indicatorInfo.url || '', {
url = params.indicatorInfo.url; domainId: `${domainId}`,
} modelId: `${modelId}`,
if (params?.indicatorInfo?.starType) { indicatorId: `${id}`,
starType = params.indicatorInfo.starType; });
} starType = params.indicatorInfo.starType || 'metric';
} }
return ( return (
<> <>
<div> <div>
@@ -119,205 +156,71 @@ export const ColumnsConfig: any = (params?: {
style={{ fontWeight: 500 }} style={{ fontWeight: 500 }}
onClick={(event: any) => { onClick={(event: any) => {
if (params?.indicatorInfo?.onNameClick) { if (params?.indicatorInfo?.onNameClick) {
params?.indicatorInfo?.onNameClick(record); const state = params.indicatorInfo.onNameClick(record);
} else { if (state === false) {
history.push(`${url}${id}`); return;
}
} }
history.push(url);
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
}} }}
// href={`/webapp${url}${id}`}
> >
{name} {name}
</a> </a>
<IndicatorStar indicatorId={id} type={starType} initState={isCollect} /> <IndicatorStar indicatorId={id} type={starType} initState={isCollect} />
{/* <Tag
color={SENSITIVE_LEVEL_COLOR[sensitiveLevel]}
style={{ lineHeight: '16px', position: 'relative', top: '-1px' }}
>
{SENSITIVE_LEVEL_ENUM[sensitiveLevel]}
</Tag> */}
</Space> </Space>
</div> </div>
<div style={{ color: '#5f748d', fontSize: 14, marginTop: 5, marginLeft: 0 }}> <div style={{ color: '#5f748d', fontSize: 14, marginTop: 5, marginLeft: 0 }}>
{bizName} {bizName}
</div> </div>
{renderAliasAndClassifications(alias, classifications)}
{(alias || isArrayOfValues(classifications)) && (
<div style={{ marginTop: 8 }}>
<Space direction="vertical" size={4}>
{alias && (
<Space
size={4}
style={{ color: '#5f748d', fontSize: 12, margin: '5px 0 5px 0' }}
>
<ReadOutlined />
<div style={{ width: 'max-content' }}>:</div>
<span style={{ marginLeft: 2 }}>
<Space size={[0, 8]} wrap>
{isString(alias) &&
alias.split(',').map((aliasName: string) => {
return (
<Tag
color="#eee"
key={aliasName}
style={{
borderRadius: 44,
maxWidth: 90,
minWidth: 40,
backgroundColor: 'rgba(18, 31, 67, 0.04)',
}}
>
<Text
style={{
maxWidth: 80,
color: 'rgb(95, 116, 141)',
textAlign: 'center',
fontSize: 12,
}}
ellipsis={{ tooltip: aliasName }}
>
{aliasName}
</Text>
</Tag>
);
})}
</Space>
</span>
</Space>
)}
{isArrayOfValues(classifications) && (
<Space
size={4}
style={{ color: '#5f748d', fontSize: 12, margin: '5px 0 5px 0' }}
>
<TagsOutlined />
<div style={{ width: 'max-content' }}>:</div>
<span style={{ marginLeft: 2 }}>
<Space size={[0, 8]} wrap>
{classifications.map((tag: string) => {
return (
<Tag
color="#eee"
key={tag}
style={{
borderRadius: 44,
maxWidth: 90,
minWidth: 40,
backgroundColor: 'rgba(18, 31, 67, 0.04)',
}}
>
<Text
style={{
maxWidth: 80,
color: 'rgb(95, 116, 141)',
textAlign: 'center',
fontSize: 12,
}}
ellipsis={{ tooltip: tag }}
>
{tag}
</Text>
</Tag>
);
})}
</Space>
</span>
</Space>
)}
{/* <Space size={10}>
<Space
size={2}
style={{ color: '#5f748d', fontSize: 12, position: 'relative', top: '1px' }}
>
<FieldNumberOutlined style={{ fontSize: 16, position: 'relative', top: '1px' }} />
<span>:</span>
<span style={{ marginLeft: 0 }}>{id}</span>
</Space>
<Space size={2} style={{ color: '#5f748d', fontSize: 12 }}>
<UserOutlined />
<span>:</span>
<span style={{ marginLeft: 0 }}>{createdBy}</span>
</Space>
</Space> */}
</Space>
</div>
)}
</> </>
); );
}, },
}, },
sensitiveLevel: { sensitiveLevel: {
render: (_, record: ISemantic.IMetricItem) => { render: (_, record: ISemantic.IMetricItem) => (
const { sensitiveLevel } = record; <Tag
return SENSITIVE_LEVEL_COLOR[sensitiveLevel] ? ( color={SENSITIVE_LEVEL_COLOR[record.sensitiveLevel] || 'default'}
<Tag style={{
color={SENSITIVE_LEVEL_COLOR[sensitiveLevel]} borderRadius: '40px',
style={{ padding: '2px 16px',
borderRadius: '40px', fontSize: '13px',
padding: '2px 16px', }}
fontSize: '13px', >
// color: '#8ca3ba', {SENSITIVE_LEVEL_ENUM[record.sensitiveLevel] || '未知'}
}} </Tag>
> ),
{SENSITIVE_LEVEL_ENUM[sensitiveLevel]}
</Tag>
) : (
<Tag
style={{
borderRadius: '40px',
padding: '2px 16px',
fontSize: '13px',
}}
>
</Tag>
);
},
}, },
state: { state: {
render: (status) => { render: (status) => {
let tagProps: { color: string; label: string; style?: any } = { const tagProps = {
color: 'default', color: 'default',
label: '未知', label: '未知',
style: {}, style: {},
}; };
switch (status) { switch (status) {
case StatusEnum.ONLINE: case StatusEnum.ONLINE:
tagProps = { tagProps.color = 'geekblue';
// color: 'processing', tagProps.label = '已启用';
color: 'geekblue',
label: '已启用',
};
break; break;
case StatusEnum.OFFLINE: case StatusEnum.OFFLINE:
tagProps = { tagProps.color = 'default';
color: 'default', tagProps.label = '未启用';
label: '未启用', tagProps.style = { color: 'rgb(95, 116, 141)', fontWeight: 400 };
style: {
color: 'rgb(95, 116, 141)',
fontWeight: 400,
},
};
break; break;
case StatusEnum.INITIALIZED: case StatusEnum.INITIALIZED:
tagProps = { tagProps.color = 'processing';
color: 'processing', tagProps.label = '初始化';
label: '初始化',
};
break; break;
case StatusEnum.DELETED: case StatusEnum.DELETED:
tagProps = { tagProps.color = 'default';
color: 'default', tagProps.label = '已删除';
label: '已删除',
};
break; break;
case StatusEnum.UNAVAILABLE: case StatusEnum.UNAVAILABLE:
tagProps = { tagProps.color = 'default';
color: 'default', tagProps.label = '不可用';
label: '不可用',
};
break; break;
default: default:
break; break;
@@ -344,14 +247,12 @@ export const ColumnsConfig: any = (params?: {
tooltip: '创建人/更新时间', tooltip: '创建人/更新时间',
width: 180, width: 180,
search: false, search: false,
render: (value: any, record: ISemantic.IMetricItem) => { render: (value: any, record: ISemantic.IMetricItem) => (
return ( <Space direction="vertical">
<Space direction="vertical"> <span> {record.createdBy}</span>
<span> {record.createdBy}</span> <span>{value && value !== '-' ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-'}</span>
<span>{value && value !== '-' ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '-'}</span> </Space>
</Space> ),
);
},
}, },
}; };
}; };

View File

@@ -6,6 +6,7 @@
.projectManger { .projectManger {
border-top: 1px solid #eee;
width: 100%; width: 100%;
min-height: calc(100vh - 56px); min-height: calc(100vh - 56px);
background-color: #fff; background-color: #fff;
@@ -163,8 +164,8 @@
// } // }
// } // }
.tab { .tab {
border-top: 1px solid #eee;
margin-top: 10px; // margin-top: 10px;
:global { :global {
.ant-tabs-tab-btn { .ant-tabs-tab-btn {
font-size: 16px; font-size: 16px;
@@ -338,12 +339,14 @@
.breadcrumb{ .breadcrumb{
font-size: 18px; font-size: 18px;
margin: 17px 0 0 20px; height: 48px;
padding-bottom: 3px; line-height: 48px;
padding: 0 20px;
:global { :global {
.ant-breadcrumb-link { .ant-breadcrumb-link {
height: 28px; height: 28px;
color: #709bf1; color: #709bf1;
cursor: pointer;
&:hover{ &:hover{
color: #296df3; color: #296df3;
} }
@@ -351,6 +354,14 @@
.anticon { .anticon {
font-size: 18px; font-size: 18px;
} }
li {
&:last-child {
.ant-breadcrumb-link {
color: #296df3;
}
}
}
} }
} }

View File

@@ -1,44 +1,28 @@
import { message } from 'antd'; import { message } from 'antd';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { history, useParams, useModel } from '@umijs/max'; import { history, useParams, useModel, Outlet } from '@umijs/max';
import DomainListTree from './components/DomainList'; import DomainListTree from './components/DomainList';
import styles from './components/style.less'; import styles from './components/style.less';
import { LeftOutlined, RightOutlined } from '@ant-design/icons'; import { LeftOutlined, RightOutlined } from '@ant-design/icons';
import { ISemantic } from './data'; import { ISemantic } from './data';
import { getDomainList, getDataSetList } from './service'; import { getDomainList, getDataSetList, getModelDetail } from './service';
import { isArrayOfValues } from '@/utils/utils'; import PageBreadcrumb from './PageBreadcrumb';
import OverviewContainerRight from './OverviewContainerRight';
type Props = { type Props = {};
mode: 'domain';
};
const OverviewContainer: React.FC<Props> = ({ mode = 'domain' }) => { const SemanticModel: React.FC<Props> = ({}) => {
const defaultTabKey = 'overview';
const params: any = useParams(); const params: any = useParams();
const domainId = params.domainId; const domainId = params.domainId;
const modelId = params.modelId; const modelId = params.modelId;
const domainModel = useModel('SemanticModel.domainData'); const domainModel = useModel('SemanticModel.domainData');
const modelModel = useModel('SemanticModel.modelData'); const modelModel = useModel('SemanticModel.modelData');
const dimensionModel = useModel('SemanticModel.dimensionData');
const metricModel = useModel('SemanticModel.metricData');
const databaseModel = useModel('SemanticModel.databaseData'); const databaseModel = useModel('SemanticModel.databaseData');
const { selectDomainId, domainList, setSelectDomain, setDomainList } = domainModel; const metricModel = useModel('SemanticModel.metricData');
const { const { setSelectDomain, setDomainList, selectDomainId } = domainModel;
selectModelId, const { selectModel, setSelectModel, setModelTableHistoryParams, MrefreshModelList } = modelModel;
modelList,
MrefreshModelList,
setSelectModel,
setModelTableHistoryParams,
} = modelModel;
const { MrefreshDimensionList } = dimensionModel;
const { MrefreshMetricList } = metricModel;
const { MrefreshDatabaseList } = databaseModel; const { MrefreshDatabaseList } = databaseModel;
const menuKey = params.menuKey ? params.menuKey : !Number(modelId) ? defaultTabKey : '';
const [collapsedState, setCollapsedState] = useState(true); const { selectMetric, setSelectMetric } = metricModel;
const [activeKey, setActiveKey] = useState<string>(menuKey);
// const [dataSetList, setDataSetList] = useState<ISemantic.IDatasetItem[]>([]);
const initSelectedDomain = (domainList: ISemantic.IDomainItem[]) => { const initSelectedDomain = (domainList: ISemantic.IDomainItem[]) => {
const targetNode = domainList.filter((item: any) => { const targetNode = domainList.filter((item: any) => {
@@ -51,8 +35,7 @@ const OverviewContainer: React.FC<Props> = ({ mode = 'domain' }) => {
if (firstRootNode) { if (firstRootNode) {
const { id } = firstRootNode; const { id } = firstRootNode;
setSelectDomain(firstRootNode); setSelectDomain(firstRootNode);
setActiveKey(menuKey); // pushUrlMenu(id, menuKey);
pushUrlMenu(id, 0, menuKey);
} }
} else { } else {
setSelectDomain(targetNode); setSelectDomain(targetNode);
@@ -69,121 +52,38 @@ const OverviewContainer: React.FC<Props> = ({ mode = 'domain' }) => {
} }
}; };
const initModelData = async () => {
const { code, data, msg } = await getModelDetail({ modelId });
if (code === 200) {
setSelectModel(data);
} else {
message.error(msg);
}
};
useEffect(() => { useEffect(() => {
initProjectTree(); initProjectTree();
MrefreshDatabaseList(); MrefreshDatabaseList();
if (modelId && modelId !== selectModel) {
initModelData();
}
return () => { return () => {
setSelectDomain(undefined); setSelectDomain(undefined);
// setSelectModel(undefined);
}; };
}, []); }, []);
useEffect(() => {
if (!selectDomainId) {
return;
}
console.log(selectDomainId, 'selectDomainIdselectDomainId');
queryModelList();
// queryDataSetList();
}, [selectDomainId]);
// const queryDataSetList = async () => {
// const { code, data, msg } = await getDataSetList(selectDomainId);
// if (code === 200) {
// setDataSetList(data);
// if (!isArrayOfValues(data)) {
// setActiveKey(defaultTabKey);
// }
// } else {
// message.error(msg);
// }
// };
const queryModelList = async () => {
await MrefreshModelList(selectDomainId);
};
// const initModelConfig = () => {
// const currentMenuKey = menuKey === defaultTabKey ? '' : menuKey;
// pushUrlMenu(selectDomainId, selectModelId, currentMenuKey);
// setActiveKey(currentMenuKey);
// };
// useEffect(() => {
// if (!selectModelId) {
// return;
// }
// // initModelConfig();
// MrefreshDimensionList({ modelId: selectModelId });
// MrefreshMetricList({ modelId: selectModelId });
// }, [selectModelId]);
const pushUrlMenu = (domainId: number, modelId: number, menuKey: string) => {
history.push(`/model/${domainId}/${menuKey}`);
};
// // const handleModelChange = (model?: ISemantic.IModelItem) => {
// // if (!model) {
// // return;
// // }
// // if (`${model.id}` === `${selectModelId}`) {
// // initModelConfig();
// // }
// // setSelectModel(model);
// // };
const cleanModelInfo = (domainId) => {
setActiveKey(defaultTabKey);
pushUrlMenu(domainId, 0, defaultTabKey);
setSelectModel(undefined);
};
const handleCollapsedBtn = () => {
setCollapsedState(!collapsedState);
};
return ( return (
<div className={styles.projectBody}> <div>
<div className={styles.projectManger}> <div style={{ background: '#fff' }}>
<div className={`${styles.sider} ${!collapsedState ? styles.siderCollapsed : ''}`}> <PageBreadcrumb />
<div className={styles.treeContainer}> </div>
<DomainListTree <div>
createDomainBtnVisible={mode === 'domain' ? true : false} <Outlet />
onTreeSelected={(domainData: ISemantic.IDomainItem) => { {/* <OverviewContainer /> */}
const { id } = domainData;
cleanModelInfo(id);
setSelectDomain(domainData);
setModelTableHistoryParams({
[id]: {},
});
}}
onTreeDataUpdate={() => {
initProjectTree();
}}
/>
</div>
<div
className={styles.siderCollapsedButton}
onClick={() => {
handleCollapsedBtn();
}}
>
{collapsedState ? <LeftOutlined /> : <RightOutlined />}
</div>
</div>
<div className={styles.content}>
{selectDomainId ? (
<>
<OverviewContainerRight />
</>
) : (
<h2 className={styles.mainTip}></h2>
)}
</div>
</div> </div>
</div> </div>
); );
}; };
export default OverviewContainer; export default SemanticModel;

View File

@@ -5,6 +5,7 @@ import { queryMetric } from '../service';
export default function Metric() { export default function Metric() {
const [metricList, setMetricList] = useState<ISemantic.IMetricItem[]>([]); const [metricList, setMetricList] = useState<ISemantic.IMetricItem[]>([]);
const [selectMetric, setSelectMetric] = useState<ISemantic.IMetricItem>();
const queryMetricList = async (params: any) => { const queryMetricList = async (params: any) => {
const { code, data, msg } = await queryMetric({ const { code, data, msg } = await queryMetric({
@@ -25,6 +26,8 @@ export default function Metric() {
return { return {
MmetricList: metricList, MmetricList: metricList,
setSelectMetric: setSelectMetric,
selectMetric: selectMetric,
MrefreshMetricList: refreshMetricList, MrefreshMetricList: refreshMetricList,
MqueryMetricList: queryMetricList, MqueryMetricList: queryMetricList,
}; };

View File

@@ -453,7 +453,7 @@ export function getUnAvailableItem(data: any): Promise<any> {
export function getModelDetail(data: any): Promise<any> { export function getModelDetail(data: any): Promise<any> {
if (!data.modelId) { if (!data.modelId) {
return; return {};
} }
return request.get(`${process.env.API_BASE_URL}model/getModel/${data.modelId}`); return request.get(`${process.env.API_BASE_URL}model/getModel/${data.modelId}`);
} }

View File

@@ -502,3 +502,10 @@ export function decryptPassword(encryptPassword: string) {
export function uniqueArray(arr: any[]) { export function uniqueArray(arr: any[]) {
return Array.from(new Set(arr)); return Array.from(new Set(arr));
} }
// 替换以:开头标记的变量
export const replaceRouteParams = (template: string, values: Record<string, string>): string => {
return template.replace(/:([a-zA-Z0-9_]+)/g, (match, key) => {
return values[key] !== undefined ? values[key] : match;
});
};