Merge remote-tracking branch 'origin/master'

This commit is contained in:
lxwcodemonkey
2024-11-30 20:57:28 +08:00
42 changed files with 1889 additions and 997 deletions

View File

@@ -91,6 +91,17 @@ const ROUTES = [
}, },
], ],
}, },
{
path: '/model/dataset/:domainId/:datasetId',
component: './SemanticModel/View/components/Detail',
envEnableList: [ENV_KEY.SEMANTIC],
routes: [
{
path: '/model/dataset/:domainId/:datasetId/:menuKey',
component: './SemanticModel/View/components/Detail',
},
],
},
{ {
path: '/model/metric/:domainId/:modelId/:metricId', path: '/model/metric/:domainId/:modelId/:metricId',
component: './SemanticModel/Metric/Edit', component: './SemanticModel/Metric/Edit',
@@ -102,101 +113,19 @@ const ROUTES = [
// }, // },
// ], // ],
}, },
// { {
// path: '/model/manager/', path: '/model/dimension/:domainId/:modelId/:dimensionId',
// component: './SemanticModel/OverviewContainer', component: './SemanticModel/Dimension/Detail',
// routes: [ envEnableList: [ENV_KEY.SEMANTIC],
// {
// path: '/model/manager/:domainId/:modelId',
// component: './SemanticModel/ModelManager',
// routes: [ // routes: [
// { // {
// path: '/model/manager/:domainId/:modelId/:menuKey', // path: '/model/manager/:domainId/:modelId/:menuKey',
// component: './SemanticModel/ModelManager', // 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',
// component: './SemanticModel/DomainManager',
// envEnableList: [ENV_KEY.SEMANTIC],
// },
// {
// path: '/model/:domainId/:modelId/metric',
// component: './SemanticModel/components/ModelMetric',
// envEnableList: [ENV_KEY.SEMANTIC],
// routes: [
// {
// path: '/model/:domainId/:modelId/metric/list',
// component: './SemanticModel/components/ClassMetricTable',
// envEnableList: [ENV_KEY.SEMANTIC],
// },
// ],
// },
], ],
}, },
// {
// path: '/model/',
// component: './SemanticModel/DomainManager',
// name: 'semanticModel',
// envEnableList: [ENV_KEY.SEMANTIC],
// routes: [
// {
// path: '/model/:domainId/:modelId',
// component: './SemanticModel/DomainManager',
// envEnableList: [ENV_KEY.SEMANTIC],
// },
// {
// path: '/model/:domainId/:modelId/:menuKey',
// component: './SemanticModel/DomainManager',
// envEnableList: [ENV_KEY.SEMANTIC],
// },
// {
// path: '/model/:domainId/:modelId/metric',
// component: './SemanticModel/components/ModelMetric',
// envEnableList: [ENV_KEY.SEMANTIC],
// routes: [
// {
// path: '/model/:domainId/:modelId/metric/list',
// component: './SemanticModel/components/ClassMetricTable',
// envEnableList: [ENV_KEY.SEMANTIC],
// },
// ],
// },
// ],
// },
// {
// path: '/model/:domainId/:modelId/:menuKey',
// component: './SemanticModel/DomainManager',
// name: 'semanticModel',
// envEnableList: [ENV_KEY.SEMANTIC],
// },
{ {
path: '/metric', path: '/metric',

View File

@@ -12,6 +12,7 @@ import { publicPath } from '../config/defaultSettings';
import { Copilot } from 'supersonic-chat-sdk'; import { Copilot } from 'supersonic-chat-sdk';
import { configProviderTheme } from '../config/themeSettings'; import { configProviderTheme } from '../config/themeSettings';
export { request } from './services/request'; export { request } from './services/request';
import { BASE_TITLE } from '@/common/constants';
import { ROUTE_AUTH_CODES } from '../config/routes'; import { ROUTE_AUTH_CODES } from '../config/routes';
import AppPage from './pages/index'; import AppPage from './pages/index';
@@ -106,12 +107,12 @@ export async function getInitialState(): Promise<{
// } // }
export function onRouteChange() { export function onRouteChange() {
const title = window.document.title.split('-SuperSonic')[0]; setTimeout(() => {
if (!title.includes('SuperSonic')) { let title = window.document.title;
window.document.title = `${title}-SuperSonic`; if (!title.toLowerCase().endsWith(BASE_TITLE.toLowerCase())) {
} else { window.document.title = `${title}-${BASE_TITLE}`;
window.document.title = 'SuperSonic';
} }
}, 100);
} }
export const layout: RunTimeLayoutConfig = (params) => { export const layout: RunTimeLayoutConfig = (params) => {

View File

@@ -3,6 +3,8 @@ export const AUTH_TOKEN_KEY = process.env.APP_TARGET === 'inner' ? 'TME_TOKEN' :
// 记录上次访问页面 // 记录上次访问页面
export const FROM_URL_KEY = 'FROM_URL'; export const FROM_URL_KEY = 'FROM_URL';
export const BASE_TITLE = 'Supersonic';
export const PRIMARY_COLOR = '#f87653'; export const PRIMARY_COLOR = '#f87653';
export const CHART_BLUE_COLOR = '#446dff'; export const CHART_BLUE_COLOR = '#446dff';
export const CHAT_BLUE = '#1b4aef'; export const CHAT_BLUE = '#1b4aef';

View File

@@ -0,0 +1,101 @@
import { message } from 'antd';
import React, { useState, useEffect, useRef } from 'react';
import { useParams, useModel, Helmet } from '@umijs/max';
import { BASE_TITLE } from '@/common/constants';
import { ISemantic } from '../data';
import { getDimensionList } from '../service';
import DetailContainer from '@/pages/SemanticModel/components/DetailContainer';
import DetailSider from '@/pages/SemanticModel/components/DetailContainer/DetailSider';
import { ProjectOutlined, ConsoleSqlOutlined } from '@ant-design/icons';
import DimensionInfoForm from '../components/DimensionInfoForm';
import DetailFormWrapper from '@/pages/SemanticModel/components/DetailContainer/DetailFormWrapper';
type Props = Record<string, any>;
const DataSetDetail: React.FC<Props> = () => {
const settingList = [
{
icon: <ProjectOutlined />,
key: 'basic',
text: '基本信息',
},
];
const params: any = useParams();
const detailId = params.dimensionId;
const modelId = params.modelId;
const domainId = params.domainId;
const menuKey = params.menuKey;
const [detailData, setDetailData] = useState<ISemantic.IDimensionItem>();
const dimensionModel = useModel('SemanticModel.dimensionData');
const { setSelectDimension } = dimensionModel;
const [activeMenu, setActiveMenu] = useState<any>(() => {
if (menuKey) {
const target = settingList.find((item) => item.key === menuKey);
if (target) {
return target;
}
}
return settingList[0];
});
const detailFormRef = useRef<any>();
useEffect(() => {
return () => {
setSelectDimension(undefined);
};
}, []);
useEffect(() => {
if (!detailId) {
return;
}
queryDetailData(detailId);
}, [detailId]);
const queryDetailData = async (id: number) => {
const { code, data, msg } = await getDimensionList({ ids: [id] });
if (code === 200) {
const target = data?.list?.[0];
setDetailData(target);
setSelectDimension(target);
return;
}
message.error(msg);
};
return (
<>
<Helmet title={`[数据集]${detailData?.name}-${BASE_TITLE}`} />
<DetailContainer
siderNode={
<DetailSider
menuKey={activeMenu.key}
menuList={settingList}
detailData={detailData}
onMenuKeyChange={(key: string, menu) => {
setActiveMenu(menu);
}}
/>
}
containerNode={
<DetailFormWrapper
currentMenu={activeMenu}
onSave={() => {
detailFormRef.current.onSave();
}}
>
<DimensionInfoForm
ref={detailFormRef}
modelId={modelId}
domainId={domainId}
dimensionItem={detailData}
/>
</DetailFormWrapper>
}
/>
</>
);
};
export default DataSetDetail;

View File

@@ -1,6 +1,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { history, useParams, useModel } from '@umijs/max'; import { useParams, useModel } from '@umijs/max';
import DomainManagerTab from './components/DomainManagerTab'; import DomainManagerTab from './components/DomainManagerTab';
import { toDomainList } from '@/pages/SemanticModel/utils';
type Props = {}; type Props = {};
@@ -14,16 +15,12 @@ const DomainManager: React.FC<Props> = ({}) => {
const [activeKey, setActiveKey] = useState<string>(menuKey); const [activeKey, setActiveKey] = useState<string>(menuKey);
const pushUrlMenu = (domainId: number, menuKey: string) => {
history.push(`/model/domain/${domainId}/${menuKey}`);
};
return ( return (
<DomainManagerTab <DomainManagerTab
activeKey={activeKey} activeKey={activeKey}
onMenuChange={(menuKey) => { onMenuChange={(menuKey) => {
setActiveKey(menuKey); setActiveKey(menuKey);
pushUrlMenu(selectDomainId, menuKey); toDomainList(selectDomainId, menuKey);
}} }}
/> />
); );

View File

@@ -540,7 +540,6 @@ const TagInfoCreateForm: React.FC<CreateFormProps> = ({
forceRender forceRender
width={800} width={800}
style={{ top: 48 }} style={{ top: 48 }}
// styles={{ padding: '32px 40px 48px' }}
destroyOnClose destroyOnClose
title={`${isEdit ? '编辑' : '新建'}标签`} title={`${isEdit ? '编辑' : '新建'}标签`}
maskClosable={false} maskClosable={false}

View File

@@ -1,7 +1,8 @@
import { message, Tabs, Button, Space } from 'antd'; import { message, Tabs, Button, Space } from 'antd';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { getMetricData, getDimensionList, getDrillDownDimension } from '../service'; import { getMetricData, getDimensionList, getDrillDownDimension } from '../service';
import { useParams, history } from '@umijs/max'; import { useParams, history, Helmet } from '@umijs/max';
import { BASE_TITLE } from '@/common/constants';
import styles from './style.less'; import styles from './style.less';
import { ArrowLeftOutlined } from '@ant-design/icons'; import { ArrowLeftOutlined } from '@ant-design/icons';
import MetricTrendSection from '@/pages/SemanticModel/Metric/components/MetricTrendSection'; import MetricTrendSection from '@/pages/SemanticModel/Metric/components/MetricTrendSection';
@@ -32,6 +33,9 @@ const MetricDetail: React.FC<Props> = () => {
}, [metricId]); }, [metricId]);
const queryMetricData = async (metricId: string) => { const queryMetricData = async (metricId: string) => {
if (!metricId) {
return;
}
const { code, data, msg } = await getMetricData(metricId); const { code, data, msg } = await getMetricData(metricId);
if (code === 200) { if (code === 200) {
setMetircData({ ...data }); setMetircData({ ...data });
@@ -80,7 +84,7 @@ const MetricDetail: React.FC<Props> = () => {
{ {
key: 'metricCaliberInput', key: 'metricCaliberInput',
label: '基础信息', label: '基础信息',
children: <MetricBasicInfo metircData={metircData} onQueryMetricData={queryMetricData} />, children: <MetricBasicInfo metircData={metircData} />,
}, },
{ {
key: 'metricTrend', key: 'metricTrend',
@@ -103,8 +107,20 @@ const MetricDetail: React.FC<Props> = () => {
return ( return (
<> <>
<Helmet
title={`${metircData?.id ? `[指标]${metircData?.name}-${BASE_TITLE}` : '新建指标'}`}
/>
<div className={styles.metricDetailWrapper}> <div className={styles.metricDetailWrapper}>
<div className={styles.metricDetail}> <div className={styles.metricDetail}>
<div className={styles.siderContainer}>
<MetricInfoSider
relationDimensionOptions={relationDimensionOptions}
metircData={metircData}
onDimensionRelationBtnClick={() => {
setMetricRelationModalOpenState(true);
}}
/>
</div>
<div className={styles.tabContainer}> <div className={styles.tabContainer}>
<Tabs <Tabs
defaultActiveKey="metricCaliberInput" defaultActiveKey="metricCaliberInput"
@@ -130,15 +146,6 @@ const MetricDetail: React.FC<Props> = () => {
className={styles.metricDetailTab} className={styles.metricDetailTab}
/> />
</div> </div>
<div className={styles.siderContainer}>
<MetricInfoSider
relationDimensionOptions={relationDimensionOptions}
metircData={metircData}
onDimensionRelationBtnClick={() => {
setMetricRelationModalOpenState(true);
}}
/>
</div>
</div> </div>
<DimensionAndMetricRelationModal <DimensionAndMetricRelationModal
metricItem={metircData} metricItem={metircData}

View File

@@ -1,21 +1,25 @@
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, useModel } from '@umijs/max'; import { useParams, useModel, Helmet } from '@umijs/max';
import styles from './style.less'; import { BASE_TITLE } from '@/common/constants';
import { ISemantic } from '../data'; import { ISemantic } from '../data';
import MetricInfoEditSider from './MetricInfoEditSider';
import MetricInfoCreateForm from './components/MetricInfoCreateForm'; import MetricInfoCreateForm from './components/MetricInfoCreateForm';
import { MetricSettingKey } from './constants'; import DetailContainer from '../components/DetailContainer';
import DetailSider from '../components/DetailContainer/DetailSider';
import { ProjectOutlined, ConsoleSqlOutlined } from '@ant-design/icons';
import { MetricSettingKey, MetricSettingWording } from './constants';
type Props = Record<string, any>; type Props = Record<string, any>;
const MetricDetail: React.FC<Props> = () => { const MetricDetail: React.FC<Props> = () => {
const params: any = useParams(); const params: any = useParams();
const metricId = params.metricId; const metricId = +params.metricId;
const modelId = +params.modelId;
const domainId = +params.domainId;
const [metircData, setMetircData] = useState<ISemantic.IMetricItem>(); const [metircData, setMetircData] = useState<ISemantic.IMetricItem>();
const metricModel = useModel('SemanticModel.metricData'); const metricModel = useModel('SemanticModel.metricData');
const { selectMetric, setSelectMetric } = metricModel; const { setSelectMetric } = metricModel;
const [settingKey, setSettingKey] = useState<MetricSettingKey>(MetricSettingKey.BASIC); const [settingKey, setSettingKey] = useState<MetricSettingKey>(MetricSettingKey.BASIC);
useEffect(() => { useEffect(() => {
@@ -31,7 +35,10 @@ const MetricDetail: React.FC<Props> = () => {
}; };
}, []); }, []);
const queryMetricData = async (metricId: string) => { const queryMetricData = async (metricId: number) => {
if (!metricId) {
return;
}
const { code, data, msg } = await getMetricData(metricId); const { code, data, msg } = await getMetricData(metricId);
if (code === 200) { if (code === 200) {
setMetircData({ ...data }); setMetircData({ ...data });
@@ -41,23 +48,46 @@ const MetricDetail: React.FC<Props> = () => {
message.error(msg); message.error(msg);
}; };
const settingList = [
{
icon: <ProjectOutlined />,
key: MetricSettingKey.BASIC,
text: MetricSettingWording[MetricSettingKey.BASIC],
},
{
icon: <ConsoleSqlOutlined />,
key: MetricSettingKey.SQL_CONFIG,
text: MetricSettingWording[MetricSettingKey.SQL_CONFIG],
},
];
return ( return (
<> <>
<div className={styles.metricEditWrapper}> <Helmet
<div className={styles.metricDetail}> title={`${
<div className={styles.siderContainer}> metircData?.id ? `[指标]${metircData?.name}-${BASE_TITLE}` : `新建指标-${BASE_TITLE}`
<MetricInfoEditSider }`}
onSettingKeyChange={(key: string) => { />
<DetailContainer
siderNode={
<DetailSider
menuKey={MetricSettingKey.BASIC}
menuList={settingList}
detailData={metircData}
onMenuKeyChange={(key: string) => {
setSettingKey(key); setSettingKey(key);
}} }}
metircData={metircData}
/> />
</div> }
<div className={styles.tabContainer}> containerNode={
<MetricInfoCreateForm settingKey={settingKey} metricItem={metircData} /> <MetricInfoCreateForm
</div> settingKey={settingKey}
</div> metricItem={metircData}
</div> modelId={metircData?.modelId || modelId}
domainId={metircData?.domainId || domainId}
/>
}
/>
</> </>
); );
}; };

View File

@@ -1,57 +0,0 @@
import { message } from 'antd';
import React, { useState, useEffect } from 'react';
import { getMetricData } from '../service';
import { useParams } from '@umijs/max';
import styles from './style.less';
import { ISemantic } from '../data';
import MetricInfoEditSider from './MetricInfoEditSider';
import MetricInfoCreateForm from './components/MetricInfoCreateForm';
import { MetricSettingKey } from './constants';
type Props = Record<string, any>;
const MetricDetail: React.FC<Props> = () => {
const params: any = useParams();
const metricId = params.metricId;
const [metircData, setMetircData] = useState<ISemantic.IMetricItem>();
const [settingKey, setSettingKey] = useState<MetricSettingKey>(MetricSettingKey.BASIC);
useEffect(() => {
if (!metricId) {
return;
}
queryMetricData(metricId);
}, [metricId]);
const queryMetricData = async (metricId: string) => {
const { code, data, msg } = await getMetricData(metricId);
if (code === 200) {
setMetircData({ ...data });
return;
}
message.error(msg);
};
return (
<>
<div className={styles.metricWrapper}>
<div className={styles.metricDetail}>
{/* <div className={styles.siderContainer}>
<MetricInfoEditSider
onSettingKeyChange={(key: string) => {
setSettingKey(key);
}}
metircData={metircData}
/>
</div> */}
<div className={styles.tabContainer}>
<MetricInfoCreateForm settingKey={settingKey} metricItem={metircData} />
</div>
</div>
</div>
</>
);
};
export default MetricDetail;

View File

@@ -152,8 +152,6 @@ const ClassMetricTable: React.FC<Props> = ({}) => {
}; };
const handleMetricEdit = (metricItem: ISemantic.IMetricItem) => { const handleMetricEdit = (metricItem: ISemantic.IMetricItem) => {
// setMetricItem(metricItem);
// setCreateModalVisible(true);
history.push(`/model/metric/edit/${metricItem.id}`); history.push(`/model/metric/edit/${metricItem.id}`);
}; };

View File

@@ -1,162 +0,0 @@
import { Tag, Space, Tooltip } from 'antd';
import React, { useState } from 'react';
import dayjs from 'dayjs';
import { MetricSettingKey, MetricSettingWording } from './constants';
import { basePath } from '../../../../config/defaultSettings';
import {
ExportOutlined,
SolutionOutlined,
PartitionOutlined,
ProjectOutlined,
ConsoleSqlOutlined,
SettingOutlined,
} from '@ant-design/icons';
import styles from './style.less';
import { ISemantic } from '../data';
import IndicatorStar from '../components/IndicatorStar';
type Props = {
metircData: ISemantic.IMetricItem;
onSettingKeyChange?: (key: MetricSettingKey) => void;
};
const MetricInfoEditSider: React.FC<Props> = ({ metircData, onSettingKeyChange }) => {
const [settingKey, setSettingKey] = useState<MetricSettingKey>(MetricSettingKey.BASIC);
const settingList = [
{
icon: <ProjectOutlined />,
key: MetricSettingKey.BASIC,
text: MetricSettingWording[MetricSettingKey.BASIC],
},
{
icon: <ConsoleSqlOutlined />,
key: MetricSettingKey.SQL_CONFIG,
text: MetricSettingWording[MetricSettingKey.SQL_CONFIG],
},
// {
// icon: <DashboardOutlined />,
// key: MetricSettingKey.DIMENSION_CONFIG,
// text: MetricSettingWording[MetricSettingKey.DIMENSION_CONFIG],
// },
];
return (
<div className={styles.metricInfoSider}>
<div className={styles.sectionContainer}>
{metircData?.id ? (
<div className={styles.title}>
<div className={styles.name}>
<Space>
<IndicatorStar indicatorId={metircData?.id} initState={metircData?.isCollect} />
{metircData?.name}
{metircData?.hasAdminRes && (
<span
className={styles.gotoMetricListIcon}
onClick={() => {
window.open(`${basePath}model/${metircData.domainId}/${metircData.modelId}/`);
}}
>
<Tooltip title="前往所属模型指标列表">
<ExportOutlined />
</Tooltip>
</span>
)}
</Space>
</div>
{metircData?.bizName && <div className={styles.bizName}>{metircData.bizName}</div>}
</div>
) : (
<div className={styles.createTitle}>
<Space>
<SettingOutlined />
</Space>
</div>
)}
<hr className={styles.hr} />
<div className={styles.section} style={{ padding: '16px 0' }}>
<ul className={styles.settingList}>
{settingList.map((item) => {
return (
<li
className={item.key === settingKey ? styles.active : ''}
key={item.key}
onClick={() => {
onSettingKeyChange?.(item.key);
setSettingKey(item.key);
}}
>
<div className={styles.icon}>{item.icon}</div>
<div className={styles.content}>
<span className={styles.text}> {item.text}</span>
</div>
</li>
);
})}
</ul>
</div>
{/* <hr className={styles.hr} /> */}
{metircData?.id && (
<div className={styles.section} style={{ marginTop: 'auto' }}>
<div className={styles.sectionTitleBox}>
<span className={styles.sectionTitle}>
<Space>
<SolutionOutlined />
</Space>
</span>
</div>
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>
<Space>
<Tag icon={<PartitionOutlined />} color="#3b5999">
{metircData?.modelName || '模型名为空'}
</Tag>
{metircData?.hasAdminRes && (
<span
className={styles.gotoMetricListIcon}
onClick={() => {
window.open(`${basePath}model/${metircData.domainId}/0/overview`);
}}
>
<Tooltip title="前往模型设置页">
<ExportOutlined />
</Tooltip>
</span>
)}
</Space>
</span>
</div>
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>{metircData?.createdBy}</span>
</div>
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>
{metircData?.createdAt
? dayjs(metircData?.createdAt).format('YYYY-MM-DD HH:mm:ss')
: ''}
</span>
</div>
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>
{metircData?.createdAt
? dayjs(metircData?.updatedAt).format('YYYY-MM-DD HH:mm:ss')
: ''}
</span>
</div>
</div>
)}
{/* <hr className={styles.hr} /> */}
</div>
</div>
);
};
export default MetricInfoEditSider;

View File

@@ -59,15 +59,6 @@ const MetricInfoSider: React.FC<Props> = ({
<div className={styles.sectionContainer}> <div className={styles.sectionContainer}>
<hr className={styles.hr} /> <hr className={styles.hr} />
<div className={styles.section}> <div className={styles.section}>
{/* <div className={styles.sectionTitleBox}>
<span className={styles.sectionTitle}>
<Space>
<ContainerOutlined />
基本信息
</Space>
</span>
</div> */}
<div className={styles.item}> <div className={styles.item}>
<span className={styles.itemLable}>: </span> <span className={styles.itemLable}>: </span>
<span className={styles.itemValue}> <span className={styles.itemValue}>

View File

@@ -1,4 +1,4 @@
import { Form, Input, Space, Row, Col, Switch, Flex, Tag } from 'antd'; import { Form, Input, Space, Row, Col } from 'antd';
import StandardFormRow from '@/components/StandardFormRow'; import StandardFormRow from '@/components/StandardFormRow';
import TagSelect from '@/components/TagSelect'; import TagSelect from '@/components/TagSelect';
import React, { ReactNode, useEffect } from 'react'; import React, { ReactNode, useEffect } from 'react';

View File

@@ -29,7 +29,6 @@ import {
batchCreateTag, batchCreateTag,
batchDeleteTag, batchDeleteTag,
} from '../../service'; } from '../../service';
import { ArrowLeftOutlined } from '@ant-design/icons';
import MetricMetricFormTable from '../../components/MetricMetricFormTable'; import MetricMetricFormTable from '../../components/MetricMetricFormTable';
import MetricFieldFormTable from '../../components/MetricFieldFormTable'; import MetricFieldFormTable from '../../components/MetricFieldFormTable';
import DimensionAndMetricRelationModal from '../../components/DimensionAndMetricRelationModal'; import DimensionAndMetricRelationModal from '../../components/DimensionAndMetricRelationModal';
@@ -38,9 +37,12 @@ import { createMetric, updateMetric, mockMetricAlias, getMetricTags } from '../.
import { MetricSettingKey, MetricSettingWording } from '../constants'; import { MetricSettingKey, MetricSettingWording } from '../constants';
import { ISemantic } from '../../data'; import { ISemantic } from '../../data';
import { history } from '@umijs/max'; import { history } from '@umijs/max';
import { toDomainList, toModelList } from '@/pages/SemanticModel/utils';
import globalStyles from '@/global.less'; import globalStyles from '@/global.less';
export type CreateFormProps = { export type CreateFormProps = {
modelId: number;
domainId: number;
datasourceId?: number; datasourceId?: number;
metricItem: any; metricItem: any;
settingKey: MetricSettingKey; settingKey: MetricSettingKey;
@@ -59,6 +61,8 @@ const queryParamsTypeParamsKey = {
}; };
const MetricInfoCreateForm: React.FC<CreateFormProps> = ({ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
modelId,
domainId,
datasourceId, datasourceId,
onCancel, onCancel,
settingKey, settingKey,
@@ -66,9 +70,6 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
onSubmit, onSubmit,
}) => { }) => {
const isEdit = !!metricItem?.id; const isEdit = !!metricItem?.id;
const domainId = metricItem?.domainId;
const modelId = metricItem?.modelId;
const [currentStep, setCurrentStep] = useState(0);
const formValRef = useRef({} as any); const formValRef = useRef({} as any);
const [form] = Form.useForm(); const [form] = Form.useForm();
const updateFormVal = (val: any) => { const updateFormVal = (val: any) => {
@@ -383,6 +384,9 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
} }
message.success('编辑指标成功'); message.success('编辑指标成功');
onSubmit?.(queryParams); onSubmit?.(queryParams);
if (!isEdit) {
toModelList(domainId, modelId!, 'metric');
}
return; return;
} }
message.error(msg); message.error(msg);
@@ -482,10 +486,11 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
}; };
const renderContent = () => { const renderContent = () => {
if (settingKey === MetricSettingKey.SQL_CONFIG) {
return ( return (
<>
<div <div
style={{ style={{
display: settingKey === MetricSettingKey.SQL_CONFIG ? 'block' : 'none',
marginLeft: '-24px', marginLeft: '-24px',
}} }}
> >
@@ -609,11 +614,8 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
</> </>
)} )}
</div> </div>
);
}
return ( <div style={{ display: settingKey === MetricSettingKey.BASIC ? 'block' : 'none' }}>
<>
<FormItem hidden={true} name="id" label="ID"> <FormItem hidden={true} name="id" label="ID">
<Input placeholder="id" /> <Input placeholder="id" />
</FormItem> </FormItem>
@@ -679,7 +681,9 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
</p> </p>
<p>1. </p> <p>1. </p>
<p>2. </p> <p>2. </p>
<p>3. 使使</p> <p>
3. 使使
</p>
<p>4. </p> <p>4. </p>
<p> <p>
便 便
@@ -814,6 +818,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
</FormItem> </FormItem>
</> </>
)} )}
</div>
</> </>
); );
}; };
@@ -827,7 +832,10 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
<span style={{ flex: 'auto' }}>{MetricSettingWording[settingKey]}</span> <span style={{ flex: 'auto' }}>{MetricSettingWording[settingKey]}</span>
<span style={{ flex: 'none' }}> <span style={{ flex: 'none' }}>
<Button <Button type="primary" onClick={handleSave}>
</Button>
{/* <Button
size="middle" size="middle"
type="link" type="link"
key="backListBtn" key="backListBtn"
@@ -839,7 +847,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
<ArrowLeftOutlined /> <ArrowLeftOutlined />
返回列表页 返回列表页
</Space> </Space>
</Button> </Button> */}
</span> </span>
</div> </div>
<div className={styles.infoCardContainer}> <div className={styles.infoCardContainer}>
@@ -870,13 +878,6 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
{renderContent()} {renderContent()}
</Form> </Form>
</div> </div>
<div className={styles.infoCardFooter}>
<div className={styles.infoCardFooterContainer}>
<Button type="primary" onClick={handleSave}>
</Button>
</div>
</div>
</div> </div>
<DimensionAndMetricRelationModal <DimensionAndMetricRelationModal
metricItem={metricItem} metricItem={metricItem}
@@ -898,15 +899,13 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
<Result <Result
style={{ background: '#fff' }} style={{ background: '#fff' }}
status="warning" status="warning"
subTitle="当前数据缺少度量,无法创建指标。请前往数据源配置中,将字段设置为度量" subTitle="当前数据模型缺少度量,无法创建指标。请前往模型配置中,将字段设置为度量"
extra={ extra={
<Button <Button
type="primary" type="primary"
key="console" key="console"
onClick={() => { onClick={() => {
history.replace( toDomainList(domainId, 'menuKey');
`/model/domain/manager/${domainId}/${modelId || metricItem?.modelId}/dataSource`,
);
onCancel?.(); onCancel?.();
}} }}
> >

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Outlet } from 'umi'; import { Outlet } from '@umijs/max';
const market: React.FC = () => { const market: React.FC = () => {
return ( return (

View File

@@ -183,68 +183,7 @@
} }
} }
.metricEditWrapper {
.metricDetailTab {
:global {
.ant-tabs-nav {
margin: 10px 20px 0 20px;
padding: 0 20px;
background-color: rgb(255, 255, 255);
border-radius: 8px;
transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
}
.ant-tabs-tab {
padding: 12px 0;
color: #344767;
font-weight: 500;
}
}
}
.metricDetail {
position: relative;
display: flex;
flex-direction: row;
width: 100%;
padding: 0px;
background-color: transparent;
height: 100%;
.tabContainer {
padding: 24px;
min-height: calc(100vh - 78px);
width: calc(100vw - 350px);
background-color: #fafafb;
}
.metricInfoContent {
padding: 25px;
.title {
position: relative;
margin-bottom: 12px;
color: #0e73ff;
font-weight: bold;
font-size: 16px;
&::before {
position: absolute;
top: 10px;
left: -10px;
display: block;
width: 3px;
height: 14px;
font-size: 0;
background: #0e73ff;
border: 1px solid #0e73ff;
border-radius: 2px;
content: '';
}
}
}
.siderContainer {
width: 350px;
min-height: calc(100vh - 78px);
border-radius: 6px;
padding: 24px 0 24px 24px;
}
}
}
.metricDetailWrapper { .metricDetailWrapper {
height: calc(100vh - 56px); height: calc(100vh - 56px);
@@ -276,7 +215,8 @@
.tabContainer { .tabContainer {
height: 100%; height: 100%;
min-height: calc(100vh - 78px); min-height: calc(100vh - 78px);
width: calc(100vw - 450px); // width: calc(100vw - 450px);
width: 100%;
background-color: #fafafb; background-color: #fafafb;
} }
.metricInfoContent { .metricInfoContent {
@@ -305,9 +245,8 @@
.siderContainer { .siderContainer {
width: 450px; width: 450px;
min-height: calc(100vh - 78px); min-height: calc(100vh - 78px);
margin: 10px 20px 20px 0;
background-color: rgb(255, 255, 255);
border-radius: 6px; border-radius: 6px;
padding: 10px 0 24px 24px;
box-shadow: rgba(0, 0, 0, 0.08) 6px 0px 16px 0px, rgba(0, 0, 0, 0.12) 3px 0px 6px -4px, box-shadow: rgba(0, 0, 0, 0.08) 6px 0px 16px 0px, rgba(0, 0, 0, 0.12) 3px 0px 6px -4px,
rgba(0, 0, 0, 0.05) 9px 0px 28px 8px; rgba(0, 0, 0, 0.05) 9px 0px 28px 8px;
} }
@@ -326,262 +265,5 @@
transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
} }
.metricInfoSider {
padding: 20px;
color: #344767;
background-color: #fff;
height: 100%;
border: 1px solid #e6ebf1;
border-radius: 6px;
.createTitle {
margin-bottom: 10px;
color:#344767;
font-weight: 500;
font-size: 16px;
font-family: var(--tencent-font-family);
}
.gotoMetricListIcon {
color: #3182ce;
cursor: pointer;
&:hover {
color: #5493ff;
}
}
.title {
margin-bottom: 20px;
.name {
font-weight: 600;
font-size: 18px;
}
.bizName {
margin: 5px 0 0 25px;
color: #7b809a;
font-weight: 400;
}
}
.desc {
display: block;
margin-top: 8px;
color: #7b809a;
font-weight: 500;
font-size: 14px;
line-height: 1.9;
}
.subTitle {
margin: 0px;
color: rgb(123, 128, 154);
font-weight: 700;
font-size: 14px;
line-height: 1.25;
letter-spacing: 0.03333em;
text-transform: uppercase;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
.sectionContainer {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
overflow: scroll;
overflow: hidden;
background-image: none;
border-radius: 6px;
.section {
padding: 16px;
color: rgb(52, 71, 103);
line-height: 1.25;
background: transparent;
box-shadow: none;
opacity: 1;
.sectionTitleBox {
padding: 8px 0;
color: rgb(52, 71, 103);
background: transparent;
box-shadow: none;
opacity: 1;
.sectionTitle {
margin: 0px;
color: rgb(52, 71, 103);
font-weight: 600;
font-size: 16px;
line-height: 1.625;
letter-spacing: 0.0075em;
text-transform: capitalize;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
}
.item {
display: flex;
padding-top: 8px;
padding-right: 16px;
padding-bottom: 8px;
color: rgb(52, 71, 103);
background: transparent;
box-shadow: none;
opacity: 1;
.itemLable {
min-width: fit-content;
margin: 0px;
margin-right: 10px;
color: #344767;
font-weight: 700;
font-size: 14px;
line-height: 1.5;
letter-spacing: 0.02857em;
text-transform: capitalize;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
.itemValue {
margin: 0px;
color: #7b809a;
font-weight: 400;
font-size: 14px;
line-height: 1.5;
letter-spacing: 0.02857em;
text-transform: none;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
}
}
.hr {
flex-shrink: 0;
margin: 0px;
border-color: rgb(242, 244, 247);
// border-width: 0px 0px thin;
border-style: solid;
}
.ctrlBox {
.ctrlList {
position: relative;
margin: 0px;
padding: 8px 0px;
list-style: none;
background-color: rgb(249, 250, 251);
li {
position: relative;
display: flex;
flex-grow: 1;
align-items: center;
justify-content: flex-start;
box-sizing: border-box;
min-width: 0px;
margin: 4px;
padding: 4px 16px;
color: inherit;
text-align: left;
text-decoration: none;
vertical-align: middle;
background-color: transparent;
border: 0px;
border-radius: 0px;
outline: 0px;
cursor: pointer;
transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
appearance: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-box-flex: 1;
-webkit-box-pack: start;
-webkit-box-align: center;
&:hover {
color: #3182ce;
text-decoration: none;
background-color: rgba(16, 24, 40, 0.04);
}
}
.ctrlItemIcon {
flex-shrink: 0;
min-width: unset;
margin-right: 5px;
font-size: 14px;
}
.styles.ctrlItemLable {
display: block;
margin: 0px;
font-weight: 400;
font-size: 14px;
line-height: 1.6;
}
}
}
}
}
.settingList {
list-style: none;
margin: 0px;
position: relative;
padding: 0px;
li {
-webkit-tap-highlight-color: transparent;
background-color: transparent;
outline: 0px;
border: 0px;
margin: 0px;
border-radius: 0px;
cursor: pointer;
user-select: none;
vertical-align: middle;
appearance: none;
display: flex;
flex-grow: 1;
justify-content: flex-start;
align-items: center;
position: relative;
text-decoration: none;
min-width: 0px;
box-sizing: border-box;
text-align: left;
padding: 8px 16px;
transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
&.active {
background-color: rgba(22, 119, 255, 0.08);
.icon {
color: rgb(22, 119, 255);
}
.content {
.text {
color: rgb(22, 119, 255);
}
}
}
.icon {
min-width: 32px;
color: #344767;
flex-shrink: 0;
display: inline-flex;
}
.content {
flex: 1 1 auto;
min-width: 0px;
margin-top: 4px;
margin-bottom: 4px;
.text {
margin: 0px;
color: #344767;
font-size: 16px;
// line-height: 1.57;
// font-family: var(--tencent-font-family);
font-weight: 600;
display: block;
}
}
&:hover {
text-decoration: none;
background-color: rgba(0, 0, 0, 0.04);
}
}
}

View File

@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { history, useParams, useModel } from '@umijs/max'; import { history, useParams, useModel } from '@umijs/max';
import ModelManagerTab from './components/ModelManagerTab'; import ModelManagerTab from './components/ModelManagerTab';
import { toModelList } from '@/pages/SemanticModel/utils';
type Props = {}; type Props = {};
@@ -21,7 +22,7 @@ const ModelManager: React.FC<Props> = ({}) => {
const initModelConfig = () => { const initModelConfig = () => {
const currentMenuKey = menuKey === defaultTabKey ? '' : menuKey; const currentMenuKey = menuKey === defaultTabKey ? '' : menuKey;
pushUrlMenu(selectDomainId, selectModelId, currentMenuKey); toModelList(selectDomainId, selectModelId!, currentMenuKey);
setActiveKey(currentMenuKey); setActiveKey(currentMenuKey);
}; };
@@ -34,17 +35,13 @@ const ModelManager: React.FC<Props> = ({}) => {
MrefreshMetricList({ modelId: selectModelId }); MrefreshMetricList({ modelId: selectModelId });
}, [selectModelId]); }, [selectModelId]);
const pushUrlMenu = (domainId: number, modelId: number, menuKey: string) => {
history.push(`/model/domain/manager/${domainId}/${modelId}/${menuKey}`);
};
return ( return (
<ModelManagerTab <ModelManagerTab
activeKey={activeKey} activeKey={activeKey}
modelList={modelList} modelList={modelList}
onMenuChange={(menuKey) => { onMenuChange={(menuKey) => {
setActiveKey(menuKey); setActiveKey(menuKey);
pushUrlMenu(selectDomainId, selectModelId, menuKey); toModelList(selectDomainId, selectModelId!, menuKey);
}} }}
/> />
); );

View File

@@ -1,9 +1,10 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { history, useParams, useModel, Outlet } from '@umijs/max'; import { 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 { toDomainList } from '@/pages/SemanticModel/utils';
type Props = {}; type Props = {};
@@ -14,10 +15,8 @@ const OverviewContainer: React.FC<Props> = ({}) => {
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 databaseModel = useModel('SemanticModel.databaseData'); const { setSelectDomain, selectDomainId } = domainModel;
const { setSelectDomain, setDomainList, selectDomainId } = domainModel;
const { setSelectModel, setModelTableHistoryParams, MrefreshModelList } = modelModel; const { setSelectModel, setModelTableHistoryParams, MrefreshModelList } = modelModel;
const { MrefreshDatabaseList } = databaseModel;
const menuKey = params.menuKey ? params.menuKey : !Number(modelId) ? defaultTabKey : ''; const menuKey = params.menuKey ? params.menuKey : !Number(modelId) ? defaultTabKey : '';
const [collapsedState, setCollapsedState] = useState(true); const [collapsedState, setCollapsedState] = useState(true);
@@ -25,51 +24,11 @@ const OverviewContainer: React.FC<Props> = ({}) => {
if (!selectDomainId || `${domainId}` === `${selectDomainId}`) { if (!selectDomainId || `${domainId}` === `${selectDomainId}`) {
return; return;
} }
pushUrlMenu(selectDomainId, menuKey); toDomainList(selectDomainId, menuKey);
}, [selectDomainId]); }, [selectDomainId]);
// const initSelectedDomain = (domainList: ISemantic.IDomainItem[]) => { const cleanModelInfo = (domainId: number) => {
// const targetNode = domainList.filter((item: any) => { toDomainList(domainId, defaultTabKey);
// 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); setSelectModel(undefined);
}; };
@@ -102,9 +61,6 @@ const OverviewContainer: React.FC<Props> = ({}) => {
[id]: {}, [id]: {},
}); });
}} }}
// onTreeDataUpdate={() => {
// // initProjectTree();
// }}
/> />
</div> </div>

View File

@@ -1,16 +1,23 @@
import { Outlet } from '@umijs/max';
import { Tabs, Breadcrumb, Space, Radio } from 'antd'; import { Tabs, Breadcrumb, Space, Radio } from 'antd';
import React, { useRef, useEffect, useState } from 'react'; import React, { useRef, useEffect, useState } from 'react';
import { history, useModel } from '@umijs/max'; import { history, useModel } from '@umijs/max';
import { HomeOutlined, FundViewOutlined } from '@ant-design/icons'; import {
HomeOutlined,
FundViewOutlined,
BarChartOutlined,
LineChartOutlined,
} from '@ant-design/icons';
import styles from './components/style.less'; import styles from './components/style.less';
import { toDomainList, toModelList } from '@/pages/SemanticModel/utils';
const PageBreadcrumb: React.FC = () => { const PageBreadcrumb: React.FC = () => {
const domainModel = useModel('SemanticModel.domainData'); const domainModel = useModel('SemanticModel.domainData');
const modelModel = useModel('SemanticModel.modelData'); const modelModel = useModel('SemanticModel.modelData');
const metricModel = useModel('SemanticModel.metricData'); const metricModel = useModel('SemanticModel.metricData');
const { selectDomainId, selectDomainName, selectDomain: domainData } = domainModel; const dimensionModel = useModel('SemanticModel.dimensionData');
const { selectDomainId, selectDomainName, selectDataSet, setSelectDataSet } = domainModel;
const { selectModelId, selectModelName, setSelectModel } = modelModel; const { selectModelId, selectModelName, setSelectModel } = modelModel;
const { selectDimension, setSelectDimension } = dimensionModel;
const { selectMetric, setSelectMetric } = metricModel; const { selectMetric, setSelectMetric } = metricModel;
@@ -20,7 +27,10 @@ const PageBreadcrumb: React.FC = () => {
<Space <Space
onClick={() => { onClick={() => {
setSelectModel(undefined); setSelectModel(undefined);
history.push(`/model/domain/${selectDomainId}/overview`); setSelectDimension(undefined);
setSelectMetric(undefined);
setSelectDataSet(undefined);
toDomainList(selectDomainId, 'overview');
}} }}
> >
<HomeOutlined /> <HomeOutlined />
@@ -30,6 +40,23 @@ const PageBreadcrumb: React.FC = () => {
}, },
]; ];
if (selectDataSet) {
items.push(
{
type: 'separator',
separator: '/',
},
{
title: (
<Space onClick={() => {}}>
<FundViewOutlined style={{ position: 'relative', top: '2px' }} />
<span>{selectDataSet.name}</span>
</Space>
),
},
);
}
if (selectModelName) { if (selectModelName) {
items.push( items.push(
{ {
@@ -40,8 +67,13 @@ const PageBreadcrumb: React.FC = () => {
title: ( title: (
<Space <Space
onClick={() => { onClick={() => {
setSelectDimension(undefined);
setSelectMetric(undefined); setSelectMetric(undefined);
history.push(`/model/domain/manager/${selectDomainId}/${selectModelId}/`); if (selectDimension) {
toModelList(selectDomainId, selectModelId!, 'dimension');
return;
}
toModelList(selectDomainId, selectModelId!, 'metric');
}} }}
> >
<FundViewOutlined style={{ position: 'relative', top: '2px' }} /> <FundViewOutlined style={{ position: 'relative', top: '2px' }} />
@@ -52,6 +84,23 @@ const PageBreadcrumb: React.FC = () => {
); );
} }
if (selectDimension) {
items.push(
{
type: 'separator',
separator: '/',
},
{
title: (
<Space onClick={() => {}}>
<BarChartOutlined style={{ position: 'relative', top: '2px' }} />
<span>{selectDimension.name}</span>
</Space>
),
},
);
}
if (selectMetric?.name) { if (selectMetric?.name) {
items.push( items.push(
{ {
@@ -61,7 +110,7 @@ const PageBreadcrumb: React.FC = () => {
{ {
title: selectMetric?.name ? ( title: selectMetric?.name ? (
<Space> <Space>
<FundViewOutlined style={{ position: 'relative', top: '2px' }} /> <LineChartOutlined style={{ position: 'relative', top: '2px' }} />
<span>{selectMetric.name}</span> <span>{selectMetric.name}</span>
</Space> </Space>
) : undefined, ) : undefined,

View File

@@ -11,6 +11,7 @@ import styles from '../../components/style.less';
import { ISemantic } from '../../data'; import { ISemantic } from '../../data';
import { ColumnsConfig } from '../../components/TableColumnRender'; import { ColumnsConfig } from '../../components/TableColumnRender';
import ViewSearchFormModal from './ViewSearchFormModal'; import ViewSearchFormModal from './ViewSearchFormModal';
import { toDatasetEditPage } from '@/pages/SemanticModel/utils';
type Props = { type Props = {
// dataSetList: ISemantic.IDatasetItem[]; // dataSetList: ISemantic.IDatasetItem[];
@@ -90,9 +91,10 @@ const DataSetTable: React.FC<Props> = ({ disabledEdit = false }) => {
return ( return (
<a <a
onClick={() => { onClick={() => {
setEditFormStep(1); toDatasetEditPage(record.domainId, record.id, 'relation');
setViewItem(record); // setEditFormStep(1);
setCreateDataSourceModalOpen(true); // setViewItem(record);
// setCreateDataSourceModalOpen(true);
}} }}
> >
{name} {name}
@@ -143,9 +145,10 @@ const DataSetTable: React.FC<Props> = ({ disabledEdit = false }) => {
<a <a
key="metricEditBtn" key="metricEditBtn"
onClick={() => { onClick={() => {
setEditFormStep(0); toDatasetEditPage(record.domainId, record.id);
setViewItem(record); // setEditFormStep(0);
setCreateDataSourceModalOpen(true); // setViewItem(record);
// setCreateDataSourceModalOpen(true);
}} }}
> >

View File

@@ -0,0 +1,202 @@
import React, { useState, useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
import { Form, Input, Select, Spin, Space } from 'antd';
import type { Ref } from 'react';
import styles from '../../components/style.less';
import { message } from 'antd';
import { formLayout } from '@/components/FormHelper/utils';
import { createView, updateView, getDimensionList, queryMetric } from '../../service';
import { ISemantic } from '../../data';
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
import SelectTMEPerson from '@/components/SelectTMEPerson';
import ViewModelConfigTransfer from './ViewModelConfigTransfer';
const FormItem = Form.Item;
export type ModelCreateFormModalProps = {
activeKey: string;
domainId: number;
datasetItem: any;
modelList: ISemantic.IModelItem[];
onCancel: () => void;
onSubmit: (values: any) => void;
};
const DatasetCreateForm: React.FC<ModelCreateFormModalProps> = forwardRef(
(
{ activeKey, datasetItem, domainId, onCancel, onSubmit, modelList }: ModelCreateFormModalProps,
ref: Ref<any>,
) => {
const [saveLoading, setSaveLoading] = useState<boolean>(false);
const [dimensionLoading, setDimensionLoading] = useState<boolean>(false);
const [selectedModelItem, setSelectedModelItem] = useState<ISemantic.IModelItem | undefined>(
modelList[0],
);
const [form] = Form.useForm();
const configTableRef = useRef<any>();
useImperativeHandle(ref, () => ({
onSave: () => {
return handleConfirm();
},
}));
useEffect(() => {
if (Array.isArray(modelList) || !selectedModelItem) {
setSelectedModelItem(modelList[0]);
}
}, [modelList]);
useEffect(() => {
form.setFieldsValue({
...datasetItem,
});
}, [datasetItem]);
const [dimensionList, setDimensionList] = useState<ISemantic.IDimensionItem[]>();
const [metricList, setMetricList] = useState<ISemantic.IMetricItem[]>();
useEffect(() => {
if (selectedModelItem?.id) {
queryDimensionList(selectedModelItem.id);
queryMetricList(selectedModelItem.id);
}
}, [selectedModelItem]);
const queryDimensionList = async (modelId: number) => {
setDimensionLoading(true);
const { code, data, msg } = await getDimensionList({ modelId });
setDimensionLoading(false);
if (code === 200 && Array.isArray(data?.list)) {
setDimensionList(data.list);
} else {
message.error(msg);
}
};
const queryMetricList = async (modelId: number) => {
const { code, data, msg } = await queryMetric({ modelId });
if (code === 200 && Array.isArray(data?.list)) {
setMetricList(data.list);
} else {
message.error(msg);
}
};
const handleConfirm = async () => {
const fieldsValue = await form.validateFields();
const viewModelConfigsMap = configTableRef?.current.getViewModelConfigs() || {};
const queryData: ISemantic.IModelItem = {
...datasetItem,
...fieldsValue,
dataSetDetail: {
dataSetModelConfigs: Object.values(viewModelConfigsMap),
},
domainId,
};
setSaveLoading(true);
const { code, msg } = await (!queryData.id ? createView : updateView)(queryData);
setSaveLoading(false);
if (code === 200) {
onSubmit?.(queryData);
} else {
message.error(msg);
}
};
const renderContent = () => {
return (
<>
<div style={{ display: activeKey === 'relation' ? 'block' : 'none' }}>
<Spin spinning={dimensionLoading}>
<ViewModelConfigTransfer
toolbarSolt={
<Space>
<span>: </span>
<Select
style={{
minWidth: 150,
textAlign: 'left',
}}
value={selectedModelItem?.id}
placeholder="请选择模型,获取当前模型下指标维度信息"
onChange={(val) => {
setDimensionList(undefined);
setMetricList(undefined);
const modelItem = modelList.find((item) => item.id === val);
setSelectedModelItem(modelItem);
}}
options={modelList.map((item) => {
return { label: item.name, value: item.id };
})}
/>
</Space>
}
dimensionList={dimensionList}
metricList={metricList}
modelItem={selectedModelItem}
viewItem={datasetItem}
ref={configTableRef}
/>
</Spin>
</div>
<div style={{ display: activeKey === 'basic' ? 'block' : 'none' }}>
<FormItem
name="name"
label="数据集名称"
rules={[{ required: true, message: '请输入数据集名称!' }]}
>
<Input placeholder="数据集名称不可重复" />
</FormItem>
<FormItem
name="bizName"
label="数据集英文名称"
rules={[{ required: true, message: '请输入数据集英文名称!' }]}
>
<Input placeholder="请输入数据集英文名称" />
</FormItem>
{/* <FormItem
name="alias"
label="别名"
getValueFromEvent={(value) => {
return Array.isArray(value) ? value.join(',') : '';
}}
getValueProps={(value) => {
return {
value: isString(value) ? value.split(',') : [],
};
}}
>
<Select
mode="tags"
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
tokenSeparators={[',']}
maxTagCount={9}
/>
</FormItem> */}
<FormItem name="admins" label={<FormItemTitle title={'责任人'} />}>
<SelectTMEPerson placeholder="请邀请团队成员" />
</FormItem>
<FormItem name="description" label="数据集描述">
<Input.TextArea placeholder="数据集描述" />
</FormItem>
</div>
</>
);
};
return (
<>
<Form
{...formLayout}
form={form}
onValuesChange={(value, values) => {}}
className={styles.form}
>
{renderContent()}
</Form>
</>
);
},
);
export default DatasetCreateForm;

View File

@@ -0,0 +1,122 @@
import { message, Form } from 'antd';
import React, { useState, useEffect, useRef } from 'react';
import { useParams, useModel, Helmet } from '@umijs/max';
import { BASE_TITLE } from '@/common/constants';
import { ISemantic } from '../../data';
import { getAllModelByDomainId, getDataSetDetail } from '../../service';
import DetailContainer from '@/pages/SemanticModel/components/DetailContainer';
import DetailSider from '@/pages/SemanticModel/components/DetailContainer/DetailSider';
import { ProjectOutlined, ConsoleSqlOutlined } from '@ant-design/icons';
import DatasetCreateForm from './DatasetCreateForm';
import DetailFormWrapper from '@/pages/SemanticModel/components/DetailContainer/DetailFormWrapper';
type Props = Record<string, any>;
const DataSetDetail: React.FC<Props> = () => {
const settingList = [
{
icon: <ProjectOutlined />,
key: 'basic',
text: '基本信息',
},
{
icon: <ConsoleSqlOutlined />,
key: 'relation',
text: '关联信息',
},
];
const params: any = useParams();
const detailId = params.datasetId;
const menuKey = params.menuKey;
const [detailData, setDetailData] = useState<ISemantic.IDatasetItem>();
const domainModel = useModel('SemanticModel.domainData');
const { selectDomainId, setSelectDataSet } = domainModel;
const [modelList, setModelList] = useState<ISemantic.IModelItem[]>([]);
const [activeMenu, setActiveMenu] = useState<any>(() => {
if (menuKey) {
const target = settingList.find((item) => item.key === menuKey);
if (target) {
return target;
}
}
return settingList[0];
});
const detailFormRef = useRef<any>();
useEffect(() => {
return () => {
setSelectDataSet(undefined);
};
}, []);
useEffect(() => {
if (!detailId) {
return;
}
queryDetailData(detailId);
}, [detailId]);
useEffect(() => {
if (!selectDomainId) {
return;
}
queryDomainAllModel();
}, [selectDomainId]);
const queryDomainAllModel = async () => {
const { code, data, msg } = await getAllModelByDomainId(selectDomainId);
if (code === 200) {
setModelList(data);
} else {
message.error(msg);
}
};
const queryDetailData = async (id: number) => {
const { code, data, msg } = await getDataSetDetail(id);
if (code === 200) {
setDetailData(data);
setSelectDataSet(data);
return;
}
message.error(msg);
};
return (
<>
<Helmet title={`[数据集]${detailData?.name}-${BASE_TITLE}`} />
<DetailContainer
siderNode={
<DetailSider
menuKey={activeMenu.key}
menuList={settingList}
detailData={detailData}
onMenuKeyChange={(key: string, menu) => {
// setSettingKey(key);
setActiveMenu(menu);
}}
/>
}
containerNode={
<DetailFormWrapper
currentMenu={activeMenu}
onSave={() => {
detailFormRef.current.onSave();
}}
>
<DatasetCreateForm
ref={detailFormRef}
activeKey={activeMenu.key}
domainId={selectDomainId}
datasetItem={detailData}
modelList={modelList}
/>
</DetailFormWrapper>
}
/>
</>
);
};
export default DataSetDetail;

View File

@@ -165,7 +165,6 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
</> </>
); );
}; };
const renderContent = () => { const renderContent = () => {
return ( return (
<> <>
@@ -262,7 +261,6 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
<Steps style={{ marginBottom: 28 }} size="small" current={currentStep}> <Steps style={{ marginBottom: 28 }} size="small" current={currentStep}>
<Step title="基本信息" /> <Step title="基本信息" />
<Step title="关联信息" /> <Step title="关联信息" />
{/* <Step title="进阶设置" /> */}
</Steps> </Steps>
<Form <Form
{...formLayout} {...formLayout}

View File

@@ -161,7 +161,7 @@ const ViewModelConfigTransfer: React.FC<Props> = forwardRef(
setSelectedTransferKeys(transferKeys); setSelectedTransferKeys(transferKeys);
setViewModelConfigsMap(viewConfigMap); setViewModelConfigsMap(viewConfigMap);
} }
}, [queryType]); }, [queryType, viewItem]);
useEffect(() => { useEffect(() => {
if (queryType !== TransType.METRIC) { if (queryType !== TransType.METRIC) {

View File

@@ -2,7 +2,7 @@ import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { ProTable } from '@ant-design/pro-components'; import { ProTable } from '@ant-design/pro-components';
import { message, Button, Space, Popconfirm, Input, Tag, Select } from 'antd'; import { message, Button, Space, Popconfirm, Input, Tag, Select } from 'antd';
import React, { useRef, useState, useEffect } from 'react'; import React, { useRef, useState, useEffect } from 'react';
import { useModel } from '@umijs/max'; import { useModel, history } from '@umijs/max';
import { StatusEnum, SemanticNodeType } from '../enum'; import { StatusEnum, SemanticNodeType } from '../enum';
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 {
@@ -19,6 +19,7 @@ import TableHeaderFilter from '@/components/TableHeaderFilter';
import BatchCtrlDropDownButton from '@/components/BatchCtrlDropDownButton'; import BatchCtrlDropDownButton from '@/components/BatchCtrlDropDownButton';
import { ColumnsConfig } from './TableColumnRender'; import { ColumnsConfig } from './TableColumnRender';
import BatchSensitiveLevelModal from '@/components/BatchCtrlDropDownButton/BatchSensitiveLevelModal'; import BatchSensitiveLevelModal from '@/components/BatchCtrlDropDownButton/BatchSensitiveLevelModal';
import { toDimensionEditPage } from '@/pages/SemanticModel/utils';
import styles from './style.less'; import styles from './style.less';
type Props = {}; type Props = {};
@@ -80,6 +81,9 @@ const ClassDimensionTable: React.FC<Props> = ({}) => {
}; };
const queryDataSourceList = async () => { const queryDataSourceList = async () => {
if (!domainId) {
return;
}
const { code, data, msg } = await getModelList(domainId); const { code, data, msg } = await getModelList(domainId);
if (code === 200) { if (code === 200) {
setDataSourceList(data); setDataSourceList(data);
@@ -94,7 +98,7 @@ const ClassDimensionTable: React.FC<Props> = ({}) => {
useEffect(() => { useEffect(() => {
queryDataSourceList(); queryDataSourceList();
}, [modelId]); }, [domainId]);
const queryBatchUpdateStatus = async (ids: React.Key[], status: StatusEnum) => { const queryBatchUpdateStatus = async (ids: React.Key[], status: StatusEnum) => {
if (Array.isArray(ids) && ids.length === 0) { if (Array.isArray(ids) && ids.length === 0) {
@@ -137,7 +141,17 @@ const ClassDimensionTable: React.FC<Props> = ({}) => {
message.error(msg); message.error(msg);
}; };
const columnsConfig = ColumnsConfig(); // const columnsConfig = ColumnsConfig();
const columnsConfig = ColumnsConfig({
indicatorInfo: {
url: '/model/dimension/:domainId/:modelId/:indicatorId',
onNameClick: (record) => {
const { id } = record;
toDimensionEditPage(domainId, modelId!, id);
return false;
},
},
});
const columns: ProColumns[] = [ const columns: ProColumns[] = [
{ {
@@ -216,8 +230,10 @@ const ClassDimensionTable: React.FC<Props> = ({}) => {
key="dimensionEditBtn" key="dimensionEditBtn"
type="link" type="link"
onClick={() => { onClick={() => {
setDimensionItem(record); // setDimensionItem(record);
setCreateModalVisible(true); // setCreateModalVisible(true);
const { id } = record;
toDimensionEditPage(domainId, modelId!, id);
}} }}
> >
@@ -423,8 +439,9 @@ const ClassDimensionTable: React.FC<Props> = ({}) => {
key="create" key="create"
type="primary" type="primary"
onClick={() => { onClick={() => {
setDimensionItem(undefined); toDimensionEditPage(domainId, modelId!, 0);
setCreateModalVisible(true); // setDimensionItem(undefined);
// setCreateModalVisible(true);
}} }}
> >

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, history } from '@umijs/max'; import { useModel } 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,
@@ -21,6 +21,7 @@ import TableHeaderFilter from '@/components/TableHeaderFilter';
import styles from './style.less'; import styles from './style.less';
import { ISemantic } from '../data'; import { ISemantic } from '../data';
import { ColumnsConfig } from './TableColumnRender'; import { ColumnsConfig } from './TableColumnRender';
import { toMetricEditPage } from '@/pages/SemanticModel/utils';
type Props = { type Props = {
onEmptyMetricData?: () => void; onEmptyMetricData?: () => void;
@@ -32,7 +33,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, selectMetric, setSelectMetric } = metricModel; const { MrefreshMetricList, 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>();
@@ -145,8 +146,8 @@ const ClassMetricTable: React.FC<Props> = ({ onEmptyMetricData }) => {
const columnsConfig = ColumnsConfig({ const columnsConfig = ColumnsConfig({
indicatorInfo: { indicatorInfo: {
url: '/model/metric/:domainId/:modelId/:indicatorId', url: '/model/metric/:domainId/:modelId/:indicatorId',
onNameClick: (record: ISemantic.IMetricItem) => { onNameClick: (record) => {
setSelectMetric(record); setSelectMetric(record as ISemantic.IMetricItem);
}, },
}, },
}); });
@@ -243,9 +244,8 @@ const ClassMetricTable: React.FC<Props> = ({ onEmptyMetricData }) => {
type="link" type="link"
key="metricEditBtn" key="metricEditBtn"
onClick={() => { onClick={() => {
history.push(`/model/metric/${record.domainId}/${record.modelId}/${record.id}`); const { domainId, modelId, id } = record;
// setMetricItem(record); toMetricEditPage(domainId, modelId, id);
// setCreateModalVisible(true);
}} }}
> >
@@ -460,8 +460,9 @@ const ClassMetricTable: React.FC<Props> = ({ onEmptyMetricData }) => {
key="create" key="create"
type="primary" type="primary"
onClick={() => { onClick={() => {
setMetricItem(undefined); toMetricEditPage(selectDomainId, modelId!, 0);
setCreateModalVisible(true); // setMetricItem(undefined);
// setCreateModalVisible(true);
}} }}
> >

View File

@@ -0,0 +1,55 @@
import { Button } from 'antd';
import React, { useState, useEffect } from 'react';
import { MenuItem } from './type';
import styles from './style.less';
type Props = {
detailData?: any;
currentMenu: MenuItem;
onSave?: (data?: any) => void;
} & { children: React.ReactNode };
const DetailFormWrapper: React.FC<Props> = ({ children, currentMenu, onSave }) => {
const [settingKey, setSettingKey] = useState<string>(currentMenu?.key);
useEffect(() => {
if (currentMenu) {
setSettingKey(currentMenu.key);
}
}, [currentMenu]);
return (
<div className={styles.infoCard}>
<div className={styles.infoCardTitle}>
<span style={{ flex: 'auto' }}>{currentMenu?.text}</span>
<span style={{ flex: 'none' }}>
<Button
type="primary"
onClick={() => {
onSave?.();
}}
>
</Button>
{/* <Button
size="middle"
type="link"
key="backListBtn"
onClick={() => {
history.back();
}}
>
<Space>
<ArrowLeftOutlined />
返回列表页
</Space>
</Button> */}
</span>
</div>
<div className={styles.infoCardContainer}>{children}</div>
</div>
);
};
export default DetailFormWrapper;

View File

@@ -0,0 +1,154 @@
import { Tag, Space, Tooltip } from 'antd';
import React, { useState, useEffect } from 'react';
import dayjs from 'dayjs';
import {
ExportOutlined,
SolutionOutlined,
PartitionOutlined,
SettingOutlined,
} from '@ant-design/icons';
import styles from './style.less';
import IndicatorStar from '../IndicatorStar';
import { toDomainList, toModelList } from '@/pages/SemanticModel/utils';
import { MenuItem } from './type';
type Props = {
detailData: any;
menuKey: string;
menuList: MenuItem[];
onMenuKeyChange?: (key: string, item: MenuItem) => void;
};
const DetailSider: React.FC<Props> = ({ detailData, menuList, menuKey, onMenuKeyChange }) => {
const [settingKey, setSettingKey] = useState<string>(menuKey);
useEffect(() => {
if (menuKey) {
setSettingKey(menuKey);
}
}, [menuKey]);
return (
<div className={styles.DetailInfoSider}>
<div className={styles.sectionContainer}>
{detailData?.id ? (
<div className={styles.title}>
<div className={styles.name}>
<Space>
{detailData?.isCollect !== undefined ? (
<IndicatorStar indicatorId={detailData?.id} initState={detailData?.isCollect} />
) : (
<div style={{ width: 15 }}></div>
)}
{detailData?.name}
{detailData?.hasAdminRes && (
<span
className={styles.gotoMetricListIcon}
onClick={() => {
toModelList(detailData.domainId, detailData.modelId);
}}
>
<Tooltip title="前往所属模型指标列表">
<ExportOutlined />
</Tooltip>
</span>
)}
</Space>
</div>
{detailData?.bizName && <div className={styles.bizName}>{detailData.bizName}</div>}
</div>
) : (
<div className={styles.createTitle}>
<Space>
<SettingOutlined />
</Space>
</div>
)}
<hr className={styles.hr} />
<div className={styles.section} style={{ padding: '16px 0' }}>
<ul className={styles.settingList}>
{menuList.map((item) => {
return (
<li
className={item.key === settingKey ? styles.active : ''}
key={item.key}
onClick={() => {
onMenuKeyChange?.(item.key, item);
setSettingKey(item.key);
}}
>
<div className={styles.icon}>{item.icon}</div>
<div className={styles.content}>
<span className={styles.text}> {item.text}</span>
</div>
</li>
);
})}
</ul>
</div>
{detailData?.id && (
<div className={styles.section} style={{ marginTop: 'auto' }}>
<div className={styles.sectionTitleBox}>
<span className={styles.sectionTitle}>
<Space>
<SolutionOutlined />
</Space>
</span>
</div>
{detailData?.modelName && (
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>
<Space>
<Tag icon={<PartitionOutlined />} color="#3b5999">
{detailData?.modelName || '模型名为空'}
</Tag>
{detailData?.hasAdminRes && (
<span
className={styles.gotoMetricListIcon}
onClick={() => {
toDomainList(detailData.domainId, 'overview');
}}
>
<Tooltip title="前往模型设置页">
<ExportOutlined />
</Tooltip>
</span>
)}
</Space>
</span>
</div>
)}
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>{detailData?.createdBy}</span>
</div>
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>
{detailData?.createdAt
? dayjs(detailData?.createdAt).format('YYYY-MM-DD HH:mm:ss')
: ''}
</span>
</div>
<div className={styles.item}>
<span className={styles.itemLable}>: </span>
<span className={styles.itemValue}>
{detailData?.createdAt
? dayjs(detailData?.updatedAt).format('YYYY-MM-DD HH:mm:ss')
: ''}
</span>
</div>
</div>
)}
</div>
</div>
);
};
export default DetailSider;

View File

@@ -0,0 +1,22 @@
import React from 'react';
import styles from './style.less';
type Props = {
siderNode: React.ReactNode;
containerNode: React.ReactNode;
};
const DetailContainer: React.FC<Props> = ({ siderNode, containerNode }) => {
return (
<>
<div className={styles.DetailWrapper}>
<div className={styles.Detail}>
<div className={styles.siderContainer}>{siderNode}</div>
<div className={styles.tabContainer}>{containerNode}</div>
</div>
</div>
</>
);
};
export default DetailContainer;

View File

@@ -0,0 +1,338 @@
.DetailWrapper {
.Detail {
position: relative;
display: flex;
flex-direction: row;
width: 100%;
padding: 0px;
background-color: transparent;
height: 100%;
.tabContainer {
padding: 12px;
min-height: calc(100vh - 105px);
width: calc(100vw - 350px);
background-color: #fafafb;
}
.siderContainer {
width: 320px;
min-height: calc(100vh - 105px);
border-radius: 6px;
padding: 12px 0 12px 12px;
}
}
}
.DetailInfoSider {
padding: 10px;
color: #344767;
background-color: #fff;
height: 100%;
border: 1px solid #e6ebf1;
border-radius: 6px;
.createTitle {
display: flex;
margin-left: 10px;
min-height: 47px;
margin-bottom: 10px;
color:#344767;
font-weight: 500;
font-size: 16px;
font-family: var(--tencent-font-family);
}
.gotoMetricListIcon {
color: #3182ce;
cursor: pointer;
&:hover {
color: #5493ff;
}
}
.title {
margin: 10px 0;
min-height: 47px;
.name {
font-weight: 600;
font-size: 18px;
}
.bizName {
margin: 5px 0 0 25px;
color: #7b809a;
font-weight: 400;
}
}
.desc {
display: block;
margin-top: 8px;
color: #7b809a;
font-weight: 500;
font-size: 14px;
line-height: 1.9;
}
.subTitle {
margin: 0px;
color: rgb(123, 128, 154);
font-weight: 700;
font-size: 14px;
line-height: 1.25;
letter-spacing: 0.03333em;
text-transform: uppercase;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
.sectionContainer {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
overflow: scroll;
overflow: hidden;
background-image: none;
border-radius: 6px;
.section {
padding: 16px;
color: rgb(52, 71, 103);
line-height: 1.25;
background: transparent;
box-shadow: none;
opacity: 1;
.sectionTitleBox {
padding: 8px 0;
color: rgb(52, 71, 103);
background: transparent;
box-shadow: none;
opacity: 1;
.sectionTitle {
margin: 0px;
color: rgb(52, 71, 103);
font-weight: 600;
font-size: 16px;
line-height: 1.625;
letter-spacing: 0.0075em;
text-transform: capitalize;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
}
.item {
display: flex;
padding-top: 8px;
padding-right: 16px;
padding-bottom: 8px;
color: rgb(52, 71, 103);
background: transparent;
box-shadow: none;
opacity: 1;
.itemLable {
min-width: fit-content;
margin: 0px;
margin-right: 10px;
color: #344767;
font-weight: 700;
font-size: 14px;
line-height: 1.5;
letter-spacing: 0.02857em;
text-transform: capitalize;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
.itemValue {
margin: 0px;
color: #7b809a;
font-weight: 400;
font-size: 14px;
line-height: 1.5;
letter-spacing: 0.02857em;
text-transform: none;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
}
}
.hr {
flex-shrink: 0;
margin: 0px;
border-color: rgb(242, 244, 247);
// border-width: 0px 0px thin;
border-style: solid;
}
.ctrlBox {
.ctrlList {
position: relative;
margin: 0px;
padding: 8px 0px;
list-style: none;
background-color: rgb(249, 250, 251);
li {
position: relative;
display: flex;
flex-grow: 1;
align-items: center;
justify-content: flex-start;
box-sizing: border-box;
min-width: 0px;
margin: 4px;
padding: 4px 16px;
color: inherit;
text-align: left;
text-decoration: none;
vertical-align: middle;
background-color: transparent;
border: 0px;
border-radius: 0px;
outline: 0px;
cursor: pointer;
transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
appearance: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-box-flex: 1;
-webkit-box-pack: start;
-webkit-box-align: center;
&:hover {
color: #3182ce;
text-decoration: none;
background-color: rgba(16, 24, 40, 0.04);
}
}
.ctrlItemIcon {
flex-shrink: 0;
min-width: unset;
margin-right: 5px;
font-size: 14px;
}
.styles.ctrlItemLable {
display: block;
margin: 0px;
font-weight: 400;
font-size: 14px;
line-height: 1.6;
}
}
}
}
}
.settingList {
list-style: none;
margin: 0px;
position: relative;
padding: 0px;
li {
-webkit-tap-highlight-color: transparent;
background-color: transparent;
outline: 0px;
border: 0px;
margin: 0px;
border-radius: 0px;
cursor: pointer;
user-select: none;
vertical-align: middle;
appearance: none;
display: flex;
flex-grow: 1;
justify-content: flex-start;
align-items: center;
position: relative;
text-decoration: none;
min-width: 0px;
box-sizing: border-box;
text-align: left;
padding: 8px 16px;
transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
&.active {
background-color: rgba(22, 119, 255, 0.08);
.icon {
color: rgb(22, 119, 255);
}
.content {
.text {
color: rgb(22, 119, 255);
}
}
}
.icon {
min-width: 32px;
color: #344767;
flex-shrink: 0;
display: inline-flex;
}
.content {
flex: 1 1 auto;
min-width: 0px;
margin-top: 4px;
margin-bottom: 4px;
.text {
margin: 0px;
color: #344767;
font-size: 16px;
// line-height: 1.57;
// font-family: var(--tencent-font-family);
font-weight: 600;
display: block;
}
}
&:hover {
text-decoration: none;
background-color: rgba(0, 0, 0, 0.04);
}
}
}
.infoCard {
min-height: 100%;
background-color: rgb(255, 255, 255);
color: rgb(38, 38, 38);
transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
position: relative;
border: 1px solid rgb(230, 235, 241);
border-radius: 4px;
box-shadow: inherit;
.infoCardTitle {
display: flex;
border-bottom: 1px solid #e6ebf1;
padding: 15px 20px;
align-items: center;
color: rgb(38, 38, 38);
margin: 0px;
font-size: 16px;
font-weight: 600;
line-height: 1.57;
font-family: "tencentFont", sans-serif;
}
.infoCardContainer {
padding: 20px;
height: calc(100vh - 260px);
overflow: scroll;
}
.infoCardFooter {
border-top: 1px solid #e6ebf1;
display: flex;
align-items: center;
justify-content: flex-end;
flex: 0 0 auto;
padding: 20px;
.infoCardFooterContainer {
box-sizing: border-box;
display: flex;
flex-flow: wrap;
// width: 100%;
justify-content: space-between;
align-items: center;
}
}
}

View File

@@ -0,0 +1,5 @@
export type MenuItem = {
icon: React.ReactNode;
key: string;
text: string;
};

View File

@@ -0,0 +1,343 @@
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
import type { Ref } from 'react';
import { Button, Form, Input, Select, Row, Col, Space, Tooltip, Switch } from 'antd';
import { SENSITIVE_LEVEL_OPTIONS, TAG_DEFINE_TYPE } from '../constant';
import { formLayout } from '@/components/FormHelper/utils';
import SqlEditor from '@/components/SqlEditor';
import { ISemantic } from '../data';
import {
DIM_OPTIONS,
EnumDataSourceType,
PARTITION_TIME_FORMATTER,
DATE_FORMATTER,
} from '@/pages/SemanticModel/Datasource/constants';
import { InfoCircleOutlined } from '@ant-design/icons';
import {
createDimension,
updateDimension,
mockDimensionAlias,
batchCreateTag,
batchDeleteTag,
} from '../service';
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
import { message } from 'antd';
import { toModelList } from '@/pages/SemanticModel/utils';
export type CreateFormProps = {
modelId: number;
domainId: number;
dimensionItem?: ISemantic.IDimensionItem;
onCancel: () => void;
onSubmit?: (values?: any) => void;
};
const FormItem = Form.Item;
const { Option } = Select;
const { TextArea } = Input;
const DimensionInfoForm: React.FC<CreateFormProps> = forwardRef(
(
{ modelId, domainId, dimensionItem, onSubmit: handleUpdate }: CreateFormProps,
ref: Ref<any>,
) => {
const isEdit = !!dimensionItem?.id;
const [dimensionValueSettingList, setDimensionValueSettingList] = useState<
ISemantic.IDimensionValueSettingItem[]
>([]);
const [form] = Form.useForm();
const { setFieldsValue, resetFields } = form;
const [llmLoading, setLlmLoading] = useState<boolean>(false);
const [formData, setFormData] = useState<ISemantic.IDimensionItem>();
useImperativeHandle(ref, () => ({
onSave: () => {
return handleSubmit();
},
}));
const handleSubmit = async (dimValueMaps?: ISemantic.IDimensionValueSettingItem[]) => {
const fieldsValue = await form.validateFields();
await saveDimension({
...fieldsValue,
dimValueMaps: dimValueMaps || dimensionValueSettingList,
alias: Array.isArray(fieldsValue.alias) ? fieldsValue.alias.join(',') : '',
});
};
const saveDimension = async (fieldsValue: any) => {
const queryParams = {
modelId: isEdit ? dimensionItem.modelId : modelId,
type: 'categorical',
...fieldsValue,
};
let saveDimensionQuery = createDimension;
if (queryParams.id) {
saveDimensionQuery = updateDimension;
}
const { code, msg, data } = await saveDimensionQuery(queryParams);
if (code === 200) {
if (queryParams.isTag) {
queryBatchExportTag(data.id || dimensionItem?.id);
}
if (dimensionItem?.id && !queryParams.isTag) {
queryBatchDeleteTag(dimensionItem);
}
if (!isEdit) {
toModelList(domainId, modelId, 'dimension');
}
message.success('保存维度成功');
return;
}
message.error(msg);
};
const queryBatchDeleteTag = async (dimensionItem: ISemantic.IDimensionItem) => {
const { code, msg } = await batchDeleteTag([
{
itemIds: [dimensionItem.id],
tagDefineType: TAG_DEFINE_TYPE.DIMENSION,
},
]);
if (code === 200) {
return;
}
message.error(msg);
};
const queryBatchExportTag = async (id: number) => {
const { code, msg } = await batchCreateTag([
{ itemId: id, tagDefineType: TAG_DEFINE_TYPE.DIMENSION },
]);
if (code === 200) {
return;
}
message.error(msg);
};
const setFormVal = () => {
if (dimensionItem) {
const { alias } = dimensionItem;
const dimensionData = {
...dimensionItem,
alias: alias && alias.trim() ? alias.split(',') : [],
};
setFieldsValue(dimensionData);
setFormData(dimensionData);
}
};
useEffect(() => {
if (dimensionItem) {
setFormVal();
if (Array.isArray(dimensionItem.dimValueMaps)) {
setDimensionValueSettingList(dimensionItem.dimValueMaps);
} else {
setDimensionValueSettingList([]);
}
} else {
resetFields();
}
}, [dimensionItem]);
const generatorDimensionAlias = async () => {
const fieldsValue = await form.validateFields();
setLlmLoading(true);
const { code, data } = await mockDimensionAlias({
...dimensionItem,
...fieldsValue,
alias: fieldsValue.alias?.join(','),
});
setLlmLoading(false);
const formAlias = form.getFieldValue('alias');
setLlmLoading(false);
if (code === 200) {
form.setFieldValue('alias', Array.from(new Set([...formAlias, ...data])));
} else {
message.error('大语言模型解析异常');
}
};
const renderContent = () => {
return (
<>
<FormItem hidden={true} name="id" label="ID">
<Input placeholder="id" />
</FormItem>
<FormItem
name="name"
label="维度名称"
rules={[{ required: true, message: '请输入维度名称' }]}
>
<Input placeholder="名称不可重复" />
</FormItem>
<FormItem
hidden={isEdit}
name="bizName"
label="英文名称"
rules={[{ required: true, message: '请输入英文名称' }]}
>
<Input placeholder="名称不可重复" disabled={isEdit} />
</FormItem>
<FormItem label="别名">
<Row>
<Col flex="1 1 200px">
<FormItem name="alias" noStyle>
<Select
mode="tags"
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
tokenSeparators={[',']}
maxTagCount={9}
/>
</FormItem>
</Col>
{isEdit && (
<Col flex="0 1 75px">
<Button
type="link"
size="small"
loading={llmLoading}
style={{ top: '2px' }}
onClick={() => {
generatorDimensionAlias();
}}
>
<Space>
<Tooltip title="智能填充将根据维度相关信息,使用大语言模型获取维度别名">
<InfoCircleOutlined />
</Tooltip>
</Space>
</Button>
</Col>
)}
</Row>
</FormItem>
<FormItem
name="type"
label="类型"
rules={[{ required: true, message: '请选择维度类型' }]}
>
<Select placeholder="请选择维度类型">
{DIM_OPTIONS.map((item) => (
<Option key={item.value} value={item.value}>
{item.label}
</Option>
))}
</Select>
</FormItem>
{formData?.type &&
[EnumDataSourceType.PARTITION_TIME, EnumDataSourceType.TIME].includes(
formData.type as EnumDataSourceType,
) && (
<FormItem
name={['ext', 'time_format']}
label="时间格式"
rules={[{ required: true, message: '请选择时间格式' }]}
tooltip="请选择数据库中时间字段对应格式"
>
<Select placeholder="请选择维度类型">
{(formData?.type === EnumDataSourceType.TIME
? DATE_FORMATTER
: PARTITION_TIME_FORMATTER
).map((item) => (
<Option key={item} value={item}>
{item}
</Option>
))}
</Select>
</FormItem>
)}
<FormItem
name="semanticType"
label="类型"
hidden={true}
// rules={[{ required: true, message: '请选择维度类型' }]}
>
<Select placeholder="请选择维度类型">
{['CATEGORY', 'ID', 'DATE'].map((item) => (
<Option key={item} value={item}>
{item}
</Option>
))}
</Select>
</FormItem>
<FormItem
name="sensitiveLevel"
label="敏感度"
rules={[{ required: true, message: '请选择敏感度' }]}
>
<Select placeholder="请选择敏感度">
{SENSITIVE_LEVEL_OPTIONS.map((item) => (
<Option key={item.value} value={item.value}>
{item.label}
</Option>
))}
</Select>
</FormItem>
{/* <FormItem name="commonDimensionId" label="公共维度">
<Select placeholder="请绑定公共维度" allowClear options={commonDimensionOptions} />
</FormItem> */}
{/* <FormItem name="defaultValues" label="默认值">
<InfoTagList />
</FormItem> */}
<Form.Item
hidden={!!!process.env.SHOW_TAG}
label={
<FormItemTitle
title={`设为标签`}
subTitle={`如果勾选,代表维度的取值都是一种'标签',可用作对实体的圈选`}
/>
}
name="isTag"
valuePropName="checked"
getValueFromEvent={(value) => {
return value === true ? 1 : 0;
}}
getValueProps={(value) => {
return {
checked: value === 1,
};
}}
>
<Switch />
</Form.Item>
<FormItem
name="description"
label="维度描述"
rules={[{ required: true, message: '请输入维度描述' }]}
>
<TextArea placeholder="请输入维度描述" />
</FormItem>
<FormItem
name="expr"
label="表达式"
tooltip="表达式中的字段必须在创建模型的时候被标记为日期或者维度"
rules={[{ required: true, message: '请输入表达式' }]}
>
<SqlEditor height={'150px'} />
</FormItem>
</>
);
};
return (
<>
<Form
{...formLayout}
form={form}
onValuesChange={(value, values) => {
setFormData(values);
}}
>
{renderContent()}
</Form>
</>
);
},
);
export default DimensionInfoForm;

View File

@@ -11,6 +11,7 @@ import TableHeaderFilter from '@/components/TableHeaderFilter';
import moment from 'moment'; import moment from 'moment';
import styles from './style.less'; import styles from './style.less';
import { ISemantic } from '../data'; import { ISemantic } from '../data';
import { toModelList } from '@/pages/SemanticModel/utils';
type Props = { type Props = {
disabledEdit?: boolean; disabledEdit?: boolean;
@@ -105,9 +106,7 @@ const ModelTable: React.FC<Props> = ({ modelList, disabledEdit = false, onModelC
<a <a
onClick={() => { onClick={() => {
setSelectModel(record); setSelectModel(record);
toModelList(domainId, id);
history.push(`/model/domain/manager/${domainId}/${id}`);
// onModelChange?.(record);
}} }}
> >
{_} {_}

View File

@@ -13,7 +13,7 @@ import IndicatorStar, { StarType } from '../components/IndicatorStar';
interface IndicatorInfo { interface IndicatorInfo {
url?: string; url?: string;
starType?: StarType; starType?: StarType;
onNameClick?: (record: ISemantic.IMetricItem) => void | boolean; onNameClick?: (record: ISemantic.IMetricItem | ISemantic.IDimensionItem) => void | boolean;
} }
interface ColumnsConfigParams { interface ColumnsConfigParams {
@@ -116,18 +116,43 @@ export const ColumnsConfig = (params?: ColumnsConfigParams) => {
}, },
dimensionInfo: { dimensionInfo: {
render: (_, record: ISemantic.IDimensionItem) => { render: (_, record: ISemantic.IDimensionItem) => {
const { name, alias, bizName } = record; const { name, alias, bizName, id, domainId, modelId } = record;
let url = `/demension/detail/${id}`;
if (params?.indicatorInfo) {
url = replaceRouteParams(params.indicatorInfo.url || '', {
domainId: `${domainId}`,
modelId: `${modelId}`,
indicatorId: `${id}`,
});
}
return ( return (
<> <>
<div> <div>
<Space> <Space>
<span style={{ fontWeight: 500 }}>{name}</span> <a
className={styles.textLink}
style={{ fontWeight: 500 }}
onClick={(event: any) => {
if (params?.indicatorInfo?.onNameClick) {
const state = params.indicatorInfo.onNameClick(record);
if (state === false) {
return;
}
}
history.push(url);
event.preventDefault();
event.stopPropagation();
}}
>
{name}
</a>
{/* <span style={{ fontWeight: 500 }}>{name}</span> */}
</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, undefined)} {alias && renderAliasAndClassifications(alias, undefined)}
</> </>
); );
}, },
@@ -136,7 +161,7 @@ export const ColumnsConfig = (params?: ColumnsConfigParams) => {
render: (_, record: ISemantic.IMetricItem) => { render: (_, record: ISemantic.IMetricItem) => {
const { name, alias, bizName, classifications, id, isCollect, domainId, modelId } = record; const { name, alias, bizName, classifications, id, isCollect, domainId, modelId } = record;
let url = `/metric/detail/`; let url = `/metric/detail/${id}`;
let starType: StarType = 'metric'; let starType: StarType = 'metric';
if (params?.indicatorInfo) { if (params?.indicatorInfo) {
url = replaceRouteParams(params.indicatorInfo.url || '', { url = replaceRouteParams(params.indicatorInfo.url || '', {
@@ -174,7 +199,7 @@ export const ColumnsConfig = (params?: ColumnsConfigParams) => {
<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 && renderAliasAndClassifications(alias, classifications)}
</> </>
); );
}, },

View File

@@ -419,7 +419,7 @@
.infoCardTitle { .infoCardTitle {
display: flex; display: flex;
border-bottom: 1px solid #e6ebf1; border-bottom: 1px solid #e6ebf1;
padding: 20px 20px 20px 40px; padding: 15px 20px;
align-items: center; align-items: center;
color: rgb(38, 38, 38); color: rgb(38, 38, 38);
margin: 0px; margin: 0px;

View File

@@ -24,6 +24,13 @@ const SemanticModel: React.FC<Props> = ({}) => {
const { selectMetric, setSelectMetric } = metricModel; const { selectMetric, setSelectMetric } = metricModel;
// useEffect(() => {
// return () => {
// setSelectMetric(undefined);
// }
// }, [])
const initSelectedDomain = (domainList: ISemantic.IDomainItem[]) => { const initSelectedDomain = (domainList: ISemantic.IDomainItem[]) => {
const targetNode = domainList.filter((item: any) => { const targetNode = domainList.filter((item: any) => {
return `${item.id}` === domainId; return `${item.id}` === domainId;

View File

@@ -5,6 +5,7 @@ import { getDimensionList } from '../service';
export default function Dimension() { export default function Dimension() {
const [dimensionList, setDimensionList] = useState<ISemantic.IDimensionItem[]>([]); const [dimensionList, setDimensionList] = useState<ISemantic.IDimensionItem[]>([]);
const [selectDimension, setSelectDimension] = useState<ISemantic.IDimensionItem>();
const queryDimensionList = async (params: any) => { const queryDimensionList = async (params: any) => {
const { code, data, msg } = await getDimensionList({ const { code, data, msg } = await getDimensionList({
@@ -23,5 +24,10 @@ export default function Dimension() {
return await queryDimensionList(params); return await queryDimensionList(params);
}; };
return { MdimensionList: dimensionList, MrefreshDimensionList: refreshDimensionList }; return {
MdimensionList: dimensionList,
MrefreshDimensionList: refreshDimensionList,
selectDimension,
setSelectDimension,
};
} }

View File

@@ -1,16 +1,37 @@
import { ISemantic } from '../data'; import { ISemantic } from '../data';
import { useState } from 'react'; import { useState, useEffect } from 'react';
import { useModel } from '@umijs/max';
export default function Domain() { export default function Domain() {
const [selectDomain, setSelectDomain] = useState<ISemantic.IDomainItem>(); const [selectDomain, setSelectDomain] = useState<ISemantic.IDomainItem>(
{} as ISemantic.IDomainItem,
);
const [selectDataSet, setSelectDataSet] = useState<ISemantic.IDatasetItem>();
const [domainList, setDomainList] = useState<ISemantic.IDomainItem[]>([]); const [domainList, setDomainList] = useState<ISemantic.IDomainItem[]>([]);
const modelModel = useModel('SemanticModel.modelData');
const metricModel = useModel('SemanticModel.metricData');
const dimensionModel = useModel('SemanticModel.dimensionData');
const { setSelectModel } = modelModel;
const { setSelectDimension } = dimensionModel;
const { setSelectMetric } = metricModel;
useEffect(() => {
setSelectModel(undefined);
setSelectDimension(undefined);
setSelectMetric(undefined);
setSelectDataSet(undefined);
}, [selectDomain]);
return { return {
selectDomain, selectDomain,
selectDataSet,
selectDomainId: selectDomain?.id, selectDomainId: selectDomain?.id,
selectDomainName: selectDomain?.name, selectDomainName: selectDomain?.name,
domainList, domainList,
setSelectDomain, setSelectDomain,
setSelectDataSet,
setDomainList, setDomainList,
}; };
} }

View File

@@ -1,6 +1,7 @@
import { ISemantic } from '../data'; import { ISemantic } from '../data';
import { message } from 'antd'; import { message } from 'antd';
import { useState } from 'react'; import { useState, useEffect } from 'react';
import { useModel } from '@umijs/max';
import { getModelList } from '../service'; import { getModelList } from '../service';
export default function Model() { export default function Model() {
@@ -8,6 +9,17 @@ export default function Model() {
const [modelList, setModelList] = useState<ISemantic.IModelItem[]>([]); const [modelList, setModelList] = useState<ISemantic.IModelItem[]>([]);
const [modelTableHistoryParams, setModelTableHistoryParams] = useState<Record<string, any>>({}); const [modelTableHistoryParams, setModelTableHistoryParams] = useState<Record<string, any>>({});
const metricModel = useModel('SemanticModel.metricData');
const dimensionModel = useModel('SemanticModel.dimensionData');
const { setSelectDimension } = dimensionModel;
const { setSelectMetric } = metricModel;
useEffect(() => {
setSelectDimension(undefined);
setSelectMetric(undefined);
}, [selectModel]);
const mergeParams = (params: Record<string, any>) => { const mergeParams = (params: Record<string, any>) => {
setModelTableHistoryParams({ setModelTableHistoryParams({
...modelTableHistoryParams, ...modelTableHistoryParams,

View File

@@ -50,9 +50,6 @@ export function getDimensionList(data: any): Promise<any> {
...(modelId ? { modelIds: [modelId] } : {}), ...(modelId ? { modelIds: [modelId] } : {}),
}, },
}; };
if (getRunningEnv() === 'chat') {
return request.post(`${process.env.CHAT_API_BASE_URL}conf/dimension/page`, queryParams);
}
return request.post(`${process.env.API_BASE_URL}dimension/queryDimension`, queryParams); return request.post(`${process.env.API_BASE_URL}dimension/queryDimension`, queryParams);
} }
@@ -628,6 +625,12 @@ export function getDataSetList(domainId: number): Promise<any> {
}); });
} }
export function getDataSetDetail(id: number): Promise<any> {
return request(`${process.env.API_BASE_URL}dataSet/${id}`, {
method: 'GET',
});
}
export function createView(data: any): Promise<any> { export function createView(data: any): Promise<any> {
return request(`${process.env.API_BASE_URL}dataSet`, { return request(`${process.env.API_BASE_URL}dataSet`, {
method: 'POST', method: 'POST',

View File

@@ -8,6 +8,8 @@ import { ConfigParametersItem } from '../System/types';
import { TransType } from './enum'; import { TransType } from './enum';
import { isString, isBoolean } from 'lodash'; import { isString, isBoolean } from 'lodash';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { history } from '@umijs/max';
import { openNewPage } from '@/utils/utils';
const FormItem = Form.Item; const FormItem = Form.Item;
const { TextArea } = Input; const { TextArea } = Input;
@@ -242,3 +244,35 @@ export const genneratorFormItemList = (itemList: ConfigParametersItem[]) => {
export const wrapperTransTypeAndId = (exTransType: TransType, id: number) => { export const wrapperTransTypeAndId = (exTransType: TransType, id: number) => {
return `${exTransType}-${id}`; return `${exTransType}-${id}`;
}; };
export const toDomainList = (domainId: number, menuKey: string) => {
history.push(`/model/domain/${domainId}/${menuKey}`);
};
export const toModelList = (domainId: number, modelId: number, menuKey?: string) => {
history.push(`/model/domain/manager/${domainId}/${modelId}${menuKey ? `/${menuKey}` : ''}`);
};
export const toMetricEditPage = (
domainId: number,
modelId: number,
metircId: number,
menuKey?: string,
) => {
history.push(`/model/metric/${domainId}/${modelId}/${metircId}${menuKey ? `/${menuKey}` : ''}`);
};
export const toDatasetEditPage = (domainId: number, datasetId: number, menuKey?: string) => {
history.push(`/model/dataset/${domainId}/${datasetId}${menuKey ? `/${menuKey}` : ''}`);
};
export const toDimensionEditPage = (
domainId: number,
modelId: number,
dimensionId: number,
menuKey?: string,
) => {
history.push(
`/model/dimension/${domainId}/${modelId}/${dimensionId}${menuKey ? `/${menuKey}` : ''}`,
);
};

View File

@@ -509,3 +509,9 @@ export const replaceRouteParams = (template: string, values: Record<string, stri
return values[key] !== undefined ? values[key] : match; return values[key] !== undefined ? values[key] : match;
}); });
}; };
export function openNewPage(url: string) {
const newWindow: any = window.open();
newWindow.opener = null;
newWindow.location.href = url;
}