Merge pull request #29 from sevenliu1896/master

[improvement][semantic-fe] Restructured the code to extract the quest…
This commit is contained in:
tristanliu
2023-08-15 10:43:14 +08:00
committed by GitHub
48 changed files with 1104 additions and 863 deletions

6
package-lock.json generated Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "github-supersonic",
"lockfileVersion": 2,
"requires": true,
"packages": {}
}

View File

@@ -15,9 +15,9 @@ const ROUTES = [
envEnableList: [ENV_KEY.CHAT], envEnableList: [ENV_KEY.CHAT],
}, },
{ {
path: '/chatSetting/:modelId?/:menuKey?', path: '/chatSetting/model/:domainId?/:modelId?/:menuKey?',
component: './SemanticModel/ChatSetting/ChatSetting',
name: 'chatSetting', name: 'chatSetting',
component: './SemanticModel/ChatSetting',
envEnableList: [ENV_KEY.CHAT], envEnableList: [ENV_KEY.CHAT],
}, },
{ {
@@ -27,9 +27,9 @@ const ROUTES = [
envEnableList: [ENV_KEY.CHAT], envEnableList: [ENV_KEY.CHAT],
}, },
{ {
path: '/semanticModel/:modelId?/:menuKey?', path: '/semanticModel/model/:domainId?/:modelId?/:menuKey?',
component: './SemanticModel/DomainManager',
name: 'semanticModel', name: 'semanticModel',
component: './SemanticModel/ProjectManager',
envEnableList: [ENV_KEY.SEMANTIC], envEnableList: [ENV_KEY.SEMANTIC],
}, },
{ {
@@ -47,10 +47,10 @@ const ROUTES = [
}, },
{ {
path: '/', path: '/',
redirect: APP_TARGET === 'inner' ? '/semanticModel' : '/chat', redirect: APP_TARGET === 'inner' ? '/semanticModel/model/' : '/chat',
envRedirect: { envRedirect: {
[ENV_KEY.CHAT]: '/chat', [ENV_KEY.CHAT]: '/chat',
[ENV_KEY.SEMANTIC]: '/semanticModel', [ENV_KEY.SEMANTIC]: '/semanticModel/model',
}, },
}, },
{ {

View File

@@ -1,15 +1,3 @@
export const EnumTransDbType = {
mysql: 'mysql',
tdw: 'tdw',
clickhouse: 'clickhouse',
kafka: 'kafka',
binlog: 'binlog',
hbase: 'hbase',
kugou_datahub: 'kugou_datahub',
aiting_datahub: 'aiting_datahub',
http: 'http',
};
export const EnumTransModelType = { export const EnumTransModelType = {
edit: '编辑', edit: '编辑',
add: '新增', add: '新增',
@@ -29,20 +17,3 @@ export const EnumDescSensitivity = {
label: '高', label: '高',
}, },
}; };
export const EnumDbTypeOwnKeys = {
mysql: ['ip', 'port', 'dbName', 'username', 'password'],
clickhouse: ['ip', 'port', 'dbName', 'username', 'password'],
tdw: ['dbName', 'username', 'password'],
kafka: ['bootstrap', 'dbName', 'username', 'password'],
binlog: ['ip', 'port', 'dbName', 'username', 'password'],
hbase: ['config'],
kugou_datahub: ['config'],
aiting_datahub: ['config'],
http: ['url'],
};
export enum EnumDashboardType {
DIR = 0, // 目录
DASHBOARD = 1, // 看板
}

View File

@@ -1,205 +0,0 @@
import { Tabs, Popover, message } from 'antd';
import React, { useEffect, useState } from 'react';
import { connect, Helmet, useParams, history } from 'umi';
import DomainListTree from './components/DomainList';
import styles from './components/style.less';
import type { StateType } from './model';
import { DownOutlined } from '@ant-design/icons';
import EntitySection from './components/Entity/EntitySection';
import RecommendedQuestionsSection from './components/Entity/RecommendedQuestionsSection';
import { ISemantic } from './data';
import { getDomainList } from './service';
import OverView from './components/OverView';
import { findLeafNodesFromDomainList } from './utils';
import { ChatConfigType } from './enum';
import type { Dispatch } from 'umi';
type Props = {
domainManger: StateType;
dispatch: Dispatch;
};
const ChatSetting: React.FC<Props> = ({ domainManger, dispatch }) => {
const defaultTabKey = 'metric';
const params: any = useParams();
const menuKey = params.menuKey ? params.menuKey : defaultTabKey;
const modelId = params.modelId;
const { selectDomainId, selectDomainName, domainList } = domainManger;
const [modelList, setModelList] = useState<ISemantic.IDomainItem[]>([]);
const [open, setOpen] = useState(false);
const [isModel, setIsModel] = useState<boolean>(false);
const [activeKey, setActiveKey] = useState<string>(menuKey);
const handleOpenChange = (newOpen: boolean) => {
setOpen(newOpen);
};
useEffect(() => {
if (selectDomainId) {
dispatch({
type: 'domainManger/queryDimensionList',
payload: {
domainId: selectDomainId,
},
});
dispatch({
type: 'domainManger/queryMetricList',
payload: {
domainId: selectDomainId,
},
});
pushUrlMenu(selectDomainId, menuKey);
}
}, [selectDomainId]);
useEffect(() => {
if (!selectDomainId) {
return;
}
const list = findLeafNodesFromDomainList(domainList, selectDomainId);
setModelList(list);
if (Array.isArray(list) && list.length > 0) {
setIsModel(false);
pushUrlMenu(selectDomainId, 'overview');
setActiveKey('overview');
} else {
setIsModel(true);
const currentMenuKey = menuKey === 'overview' ? defaultTabKey : menuKey;
pushUrlMenu(selectDomainId, currentMenuKey);
setActiveKey(currentMenuKey);
}
}, [domainList, selectDomainId]);
const initSelectedDomain = (domainList: ISemantic.IDomainItem[]) => {
const targetNode = domainList.filter((item: any) => {
return `${item.id}` === modelId;
})[0];
if (!targetNode) {
const firstRootNode = domainList.filter((item: any) => {
return item.parentId === 0;
})[0];
if (firstRootNode) {
const { id, name } = firstRootNode;
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: id,
selectDomainName: name,
domainData: firstRootNode,
});
}
} else {
const { id, name } = targetNode;
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: id,
selectDomainName: name,
domainData: targetNode,
});
}
};
const initProjectTree = async () => {
const { code, data, msg } = await getDomainList();
if (code === 200) {
if (!selectDomainId) {
initSelectedDomain(data);
}
dispatch({
type: 'domainManger/setDomainList',
payload: { domainList: data },
});
} else {
message.error(msg);
}
};
useEffect(() => {
initProjectTree();
}, []);
const pushUrlMenu = (domainId: number, menuKey: string) => {
history.push(`/chatSetting/${domainId}/${menuKey}`);
};
const tabItem = [
{
label: '子主题域',
key: 'overview',
children: <OverView modelList={modelList} />,
},
];
const isModelItem = [
{
label: '指标模式',
key: 'metric',
children: <EntitySection chatConfigType={ChatConfigType.AGG} />,
},
{
label: '实体模式',
key: 'dimenstion',
children: <EntitySection chatConfigType={ChatConfigType.DETAIL} />,
},
{
label: '推荐问题',
key: 'recommendedQuestions',
children: <RecommendedQuestionsSection />,
},
];
return (
<div className={styles.projectBody}>
<Helmet title={'问答设置-超音数'} />
<div className={styles.projectManger}>
<h2 className={styles.title}>
<Popover
zIndex={1000}
overlayInnerStyle={{
overflow: 'scroll',
maxHeight: '800px',
}}
content={
<DomainListTree
createDomainBtnVisible={false}
onTreeSelected={() => {
setOpen(false);
}}
/>
}
trigger="click"
open={open}
onOpenChange={handleOpenChange}
>
<div className={styles.domainSelector}>
<span className={styles.domainTitle}>
{selectDomainName ? `选择的主题域:${selectDomainName}` : '主题域信息'}
</span>
<span className={styles.downIcon}>
<DownOutlined />
</span>
</div>
</Popover>
</h2>
{selectDomainId ? (
<>
<Tabs
className={styles.tab}
activeKey={activeKey}
destroyInactiveTabPane
onChange={(menuKey: string) => {
setActiveKey(menuKey);
pushUrlMenu(selectDomainId, menuKey);
}}
items={!isModel ? tabItem : isModelItem}
/>
</>
) : (
<h2 className={styles.mainTip}></h2>
)}
</div>
</div>
);
};
export default connect(({ domainManger }: { domainManger: StateType }) => ({
domainManger,
}))(ChatSetting);

View File

@@ -0,0 +1,22 @@
import React from 'react';
import { connect, Helmet } from 'umi';
import type { StateType } from '../model';
import OverviewContainer from '../OverviewContainer';
import type { Dispatch } from 'umi';
type Props = {
domainManger: StateType;
dispatch: Dispatch;
};
const ChatSetting: React.FC<Props> = () => {
return (
<>
<Helmet title={'模型管理-超音数'} />
<OverviewContainer mode={'chatSetting'} />
</>
);
};
export default connect(({ domainManger }: { domainManger: StateType }) => ({
domainManger,
}))(ChatSetting);

View File

@@ -0,0 +1,102 @@
import { Tabs, Button } from 'antd';
import React from 'react';
import { connect } from 'umi';
import styles from '../components/style.less';
import type { StateType } from '../model';
import { LeftOutlined } from '@ant-design/icons';
import EntitySection from '../components/Entity/EntitySection';
import RecommendedQuestionsSection from '../components/Entity/RecommendedQuestionsSection';
import { ISemantic } from '../data';
import OverView from '../components/OverView';
import { ChatConfigType } from '../enum';
import type { Dispatch } from 'umi';
type Props = {
isModel: boolean;
activeKey: string;
modelList: ISemantic.IModelItem[];
handleModelChange: (model?: ISemantic.IModelItem) => void;
onBackDomainBtnClick?: () => void;
onMenuChange?: (menuKey: string) => void;
domainManger: StateType;
dispatch: Dispatch;
};
const ChatSetting: React.FC<Props> = ({
isModel,
activeKey,
modelList,
handleModelChange,
onBackDomainBtnClick,
onMenuChange,
}) => {
const defaultTabKey = 'metric';
const isModelItem = [
{
label: '指标模式',
key: 'metric',
children: <EntitySection chatConfigType={ChatConfigType.AGG} />,
},
{
label: '实体模式',
key: 'dimenstion',
children: <EntitySection chatConfigType={ChatConfigType.DETAIL} />,
},
{
label: '推荐问题',
key: 'recommendedQuestions',
children: <RecommendedQuestionsSection />,
},
];
const tabItem = [
{
label: '模型',
key: 'overview',
children: (
<OverView
modelList={modelList}
disabledEdit={true}
onModelChange={(model) => {
handleModelChange(model);
}}
/>
),
},
];
return (
<>
<Tabs
className={styles.tab}
items={isModel ? isModelItem : tabItem}
activeKey={activeKey || defaultTabKey}
destroyInactiveTabPane
tabBarExtraContent={
isModel ? (
<Button
type="primary"
icon={<LeftOutlined />}
onClick={() => {
onBackDomainBtnClick?.();
}}
style={{ marginRight: 10 }}
>
</Button>
) : undefined
}
onChange={(menuKey: string) => {
onMenuChange?.(menuKey);
}}
/>
</>
);
};
export default connect(({ domainManger }: { domainManger: StateType }) => ({
domainManger,
}))(ChatSetting);

View File

@@ -10,14 +10,12 @@ import { createDatasource, updateDatasource, getColumns } from '../../service';
import type { Dispatch } from 'umi'; import type { Dispatch } from 'umi';
import type { StateType } from '../../model'; import type { StateType } from '../../model';
import { connect } from 'umi'; import { connect } from 'umi';
import { isUndefined } from 'lodash';
export type CreateFormProps = { export type CreateFormProps = {
domainManger: StateType; domainManger: StateType;
dispatch: Dispatch; dispatch: Dispatch;
createModalVisible: boolean; createModalVisible: boolean;
sql?: string; sql?: string;
domainId: number;
dataSourceItem: DataInstanceItem | any; dataSourceItem: DataInstanceItem | any;
onCancel?: () => void; onCancel?: () => void;
onSubmit?: (dataSourceInfo: any) => void; onSubmit?: (dataSourceInfo: any) => void;
@@ -37,7 +35,6 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
domainManger, domainManger,
onCancel, onCancel,
createModalVisible, createModalVisible,
domainId,
scriptColumns, scriptColumns,
sql = '', sql = '',
onSubmit, onSubmit,
@@ -51,7 +48,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
const [hasEmptyNameField, setHasEmptyNameField] = useState<boolean>(false); const [hasEmptyNameField, setHasEmptyNameField] = useState<boolean>(false);
const formValRef = useRef(initFormVal as any); const formValRef = useRef(initFormVal as any);
const [form] = Form.useForm(); const [form] = Form.useForm();
const { dataBaseConfig } = domainManger; const { dataBaseConfig, selectModelId: modelId } = domainManger;
const updateFormVal = (val: any) => { const updateFormVal = (val: any) => {
formValRef.current = val; formValRef.current = val;
}; };
@@ -163,7 +160,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
databaseId: dataSourceItem?.databaseId || dataBaseConfig.id, databaseId: dataSourceItem?.databaseId || dataBaseConfig.id,
queryType: basicInfoFormMode === 'fast' ? 'table_query' : 'sql_query', queryType: basicInfoFormMode === 'fast' ? 'table_query' : 'sql_query',
tableQuery: dbName && tableName ? `${dbName}.${tableName}` : '', tableQuery: dbName && tableName ? `${dbName}.${tableName}` : '',
domainId, modelId,
}; };
const queryDatasource = isEdit ? updateDatasource : createDatasource; const queryDatasource = isEdit ? updateDatasource : createDatasource;
const { code, msg, data } = await queryDatasource(queryParams); const { code, msg, data } = await queryDatasource(queryParams);

View File

@@ -18,7 +18,6 @@ import FullScreen from '@/components/FullScreen';
import SqlEditor from '@/components/SqlEditor'; import SqlEditor from '@/components/SqlEditor';
import type { TaskResultItem, DataInstanceItem, TaskResultColumn } from '../data'; import type { TaskResultItem, DataInstanceItem, TaskResultColumn } from '../data';
import { excuteSql } from '@/pages/SemanticModel/service'; import { excuteSql } from '@/pages/SemanticModel/service';
// import { getDatabaseByDomainId } from '../../service';
import DataSourceCreateForm from './DataSourceCreateForm'; import DataSourceCreateForm from './DataSourceCreateForm';
import type { Dispatch } from 'umi'; import type { Dispatch } from 'umi';
import type { StateType } from '../../model'; import type { StateType } from '../../model';
@@ -33,7 +32,6 @@ type IProps = {
domainManger: StateType; domainManger: StateType;
dispatch: Dispatch; dispatch: Dispatch;
dataSourceItem: DataInstanceItem; dataSourceItem: DataInstanceItem;
domainId: number;
onUpdateSql?: (sql: string) => void; onUpdateSql?: (sql: string) => void;
sql?: string; sql?: string;
onSubmitSuccess?: (dataSourceInfo: any) => void; onSubmitSuccess?: (dataSourceInfo: any) => void;
@@ -59,12 +57,11 @@ const SqlDetail: React.FC<IProps> = ({
domainManger, domainManger,
dataSourceItem, dataSourceItem,
onSubmitSuccess, onSubmitSuccess,
domainId,
sql = '', sql = '',
onUpdateSql, onUpdateSql,
onJdbcSourceChange, onJdbcSourceChange,
}) => { }) => {
const { dataBaseConfig } = domainManger; const { dataBaseConfig, selectModelId: modelId } = domainManger;
const [resultTable, setResultTable] = useState<ResultTableItem[]>([]); const [resultTable, setResultTable] = useState<ResultTableItem[]>([]);
const [resultTableLoading, setResultTableLoading] = useState(false); const [resultTableLoading, setResultTableLoading] = useState(false);
const [resultCols, setResultCols] = useState<ResultColItem[]>([]); const [resultCols, setResultCols] = useState<ResultColItem[]>([]);
@@ -82,8 +79,6 @@ const SqlDetail: React.FC<IProps> = ({
y: 200, y: 200,
}); });
// const [dataSourceResult, setDataSourceResult] = useState<any>({});
const [runState, setRunState] = useState<boolean | undefined>(); const [runState, setRunState] = useState<boolean | undefined>();
const [taskLog, setTaskLog] = useState(''); const [taskLog, setTaskLog] = useState('');
@@ -93,7 +88,6 @@ const SqlDetail: React.FC<IProps> = ({
const [isSqlIdeFullScreen, setIsSqlIdeFullScreen] = useState<boolean>(false); const [isSqlIdeFullScreen, setIsSqlIdeFullScreen] = useState<boolean>(false);
const [isSqlResFullScreen, setIsSqlResFullScreen] = useState<boolean>(false); const [isSqlResFullScreen, setIsSqlResFullScreen] = useState<boolean>(false);
// const [sqlParams, setSqlParams] = useState<SqlParamsItem[]>([]);
const resultInnerWrap = useRef<HTMLDivElement>(); const resultInnerWrap = useRef<HTMLDivElement>();
const [editorSize, setEditorSize] = useState<number>(0); const [editorSize, setEditorSize] = useState<number>(0);
@@ -104,18 +98,6 @@ const SqlDetail: React.FC<IProps> = ({
const [isRight, setIsRight] = useState(false); const [isRight, setIsRight] = useState(false);
const [scriptColumns, setScriptColumns] = useState<any[]>([]); const [scriptColumns, setScriptColumns] = useState<any[]>([]);
// const [jdbcSourceName, setJdbcSourceName] = useState<string>(() => {
// const sourceId = dataSourceItem.databaseId;
// if (sourceId) {
// const target: any = jdbcSourceItems.filter((item: any) => {
// return item.key === Number(sourceId);
// })[0];
// if (target) {
// return target.label;
// }
// }
// return 'ClickHouse';
// });
useEffect(() => { useEffect(() => {
setJdbcSourceItems([ setJdbcSourceItems([
@@ -127,21 +109,6 @@ const SqlDetail: React.FC<IProps> = ({
onJdbcSourceChange?.(dataBaseConfig?.id && Number(dataBaseConfig?.id)); onJdbcSourceChange?.(dataBaseConfig?.id && Number(dataBaseConfig?.id));
}, [dataBaseConfig]); }, [dataBaseConfig]);
// const queryDatabaseConfig = async () => {
// const { code, data } = await getDatabaseByDomainId(domainId);
// if (code === 200) {
// setJdbcSourceItems([
// {
// label: data?.name,
// key: data?.id,
// },
// ]);
// onJdbcSourceChange?.(data?.id && Number(data?.id));
// return;
// }
// message.error('数据库配置获取错误');
// };
function creatCalcItem(key: string, data: string) { function creatCalcItem(key: string, data: string) {
const line = document.createElement('div'); // 需要每条数据一行,这样避免数据换行的时候获得的宽度不准确 const line = document.createElement('div'); // 需要每条数据一行,这样避免数据换行的时候获得的宽度不准确
const child = document.createElement('span'); const child = document.createElement('span');
@@ -245,7 +212,7 @@ const SqlDetail: React.FC<IProps> = ({
setResultTableLoading(true); setResultTableLoading(true);
const { code, data, msg } = await excuteSql({ const { code, data, msg } = await excuteSql({
sql: value, sql: value,
domainId, modelId,
}); });
setResultTableLoading(false); setResultTableLoading(false);
if (code === 200) { if (code === 200) {
@@ -521,7 +488,6 @@ const SqlDetail: React.FC<IProps> = ({
{dataSourceModalVisible && ( {dataSourceModalVisible && (
<DataSourceCreateForm <DataSourceCreateForm
sql={sql} sql={sql}
domainId={domainId}
dataSourceItem={dataSourceItem} dataSourceItem={dataSourceItem}
scriptColumns={scriptColumns} scriptColumns={scriptColumns}
onCancel={() => { onCancel={() => {

View File

@@ -22,14 +22,13 @@ type TableRef = {
type Props = { type Props = {
initialValues: any; initialValues: any;
domainId: number;
onSubmitSuccess?: (dataSourceInfo: any) => void; onSubmitSuccess?: (dataSourceInfo: any) => void;
}; };
const { TabPane } = Tabs; const { TabPane } = Tabs;
const LIST_KEY = 'list'; const LIST_KEY = 'list';
const SqlSide: React.FC<Props> = ({ initialValues, domainId, onSubmitSuccess }) => { const SqlSide: React.FC<Props> = ({ initialValues, onSubmitSuccess }) => {
const defaultPanes: Panes[] = [ const defaultPanes: Panes[] = [
{ {
key: '数据源查询', key: '数据源查询',
@@ -98,8 +97,6 @@ const SqlSide: React.FC<Props> = ({ initialValues, domainId, onSubmitSuccess })
<SqlDetail <SqlDetail
onSubmitSuccess={onSubmitSuccess} onSubmitSuccess={onSubmitSuccess}
dataSourceItem={dataSourceItem} dataSourceItem={dataSourceItem}
oprType={pane.type}
domainId={domainId}
onUpdateSql={(sql: string) => { onUpdateSql={(sql: string) => {
updateTabSql(sql, pane.key); updateTabSql(sql, pane.key);
}} }}

View File

@@ -7,13 +7,12 @@ import { RightOutlined, LeftOutlined } from '@ant-design/icons';
type Props = { type Props = {
initialValues: any; initialValues: any;
domainId: number;
onSubmitSuccess?: (dataSourceInfo: any) => void; onSubmitSuccess?: (dataSourceInfo: any) => void;
}; };
const DEFAULT_RIGHT_SIZE = '300px'; const DEFAULT_RIGHT_SIZE = '300px';
const DataExploreView: React.FC<Props> = ({ initialValues, domainId, onSubmitSuccess }) => { const DataExploreView: React.FC<Props> = ({ initialValues, onSubmitSuccess }) => {
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
useEffect(() => { useEffect(() => {
@@ -51,11 +50,7 @@ const DataExploreView: React.FC<Props> = ({ initialValues, domainId, onSubmitSuc
{collapsed ? <LeftOutlined /> : <RightOutlined />} {collapsed ? <LeftOutlined /> : <RightOutlined />}
</div> </div>
)} )}
<SqlSide <SqlSide initialValues={initialValues} onSubmitSuccess={onSubmitSuccess} />
initialValues={initialValues}
domainId={domainId}
onSubmitSuccess={onSubmitSuccess}
/>
</div> </div>
<Pane initialSize={0} /> <Pane initialSize={0} />

View File

@@ -0,0 +1,22 @@
import React from 'react';
import { connect, Helmet } from 'umi';
import type { StateType } from './model';
import OverviewContainer from './OverviewContainer';
import type { Dispatch } from 'umi';
type Props = {
domainManger: StateType;
dispatch: Dispatch;
};
const DomainManager: React.FC<Props> = () => {
return (
<>
<Helmet title={'模型管理-超音数'} />
<OverviewContainer mode={'domain'} />
</>
);
};
export default connect(({ domainManger }: { domainManger: StateType }) => ({
domainManger,
}))(DomainManager);

View File

@@ -1,4 +1,4 @@
import { Form, Select, Input } from 'antd'; import { Form, Input } from 'antd';
import StandardFormRow from '@/components/StandardFormRow'; import StandardFormRow from '@/components/StandardFormRow';
import TagSelect from '@/components/TagSelect'; import TagSelect from '@/components/TagSelect';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
@@ -64,7 +64,7 @@ const MetricFilter: React.FC<Props> = ({ filterValues = {}, onFiltersChange }) =
> >
<StandardFormRow key="search" block> <StandardFormRow key="search" block>
<div className={styles.searchBox}> <div className={styles.searchBox}>
<FormItem name={'name'} noStyle> <FormItem name={'key'} noStyle>
<div className={styles.searchInput}> <div className={styles.searchInput}>
<Input.Search <Input.Search
placeholder="请输入需要查询指标的ID、指标名称、字段名称" placeholder="请输入需要查询指标的ID、指标名称、字段名称"

View File

@@ -1,16 +1,17 @@
import type { ActionType, ProColumns } from '@ant-design/pro-table'; import type { ActionType, ProColumns } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table'; import ProTable from '@ant-design/pro-table';
import { message } from 'antd'; import { message, Space, Popconfirm } from 'antd';
import React, { useRef, useState, useEffect } from 'react'; import React, { useRef, useState, useEffect } from 'react';
import type { Dispatch } from 'umi'; import type { Dispatch } from 'umi';
import { connect } from 'umi'; import { connect } from 'umi';
import type { StateType } from '../model'; import type { StateType } from '../model';
import { SENSITIVE_LEVEL_ENUM } from '../constant'; import { SENSITIVE_LEVEL_ENUM } from '../constant';
import { queryMetric } from '../service'; import { queryMetric, deleteMetric } from '../service';
import MetricFilter from './components/MetricFilter'; import MetricFilter from './components/MetricFilter';
import MetricInfoCreateForm from '../components/MetricInfoCreateForm';
import moment from 'moment'; import moment from 'moment';
import styles from './style.less'; import styles from './style.less';
import { IDataSource, ISemantic } from '../data';
type Props = { type Props = {
dispatch: Dispatch; dispatch: Dispatch;
@@ -26,14 +27,17 @@ type QueryMetricListParams = {
[key: string]: any; [key: string]: any;
}; };
const ClassMetricTable: React.FC<Props> = () => { const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
const { selectDomainId } = domainManger;
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState({
current: 1, current: 1,
pageSize: 20, pageSize: 20,
total: 0, total: 0,
}); });
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [dataSource, setDataSource] = useState<any[]>([]); const [dataSource, setDataSource] = useState<IDataSource.IDataSourceItem[]>([]);
const [metricItem, setMetricItem] = useState<ISemantic.IMetricItem>();
const [filterParams, setFilterParams] = useState<Record<string, any>>({}); const [filterParams, setFilterParams] = useState<Record<string, any>>({});
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
@@ -92,8 +96,8 @@ const ClassMetricTable: React.FC<Props> = () => {
title: '字段名称', title: '字段名称',
}, },
{ {
dataIndex: 'domainName', dataIndex: 'modelName',
title: '主题域', title: '所属模型',
}, },
{ {
dataIndex: 'sensitiveLevel', dataIndex: 'sensitiveLevel',
@@ -127,6 +131,50 @@ const ClassMetricTable: React.FC<Props> = () => {
return value && value !== '-' ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-'; return value && value !== '-' ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-';
}, },
}, },
{
title: '操作',
dataIndex: 'x',
valueType: 'option',
render: (_, record) => {
return (
<Space>
<a
key="metricEditBtn"
onClick={() => {
setMetricItem(record);
setCreateModalVisible(true);
}}
>
</a>
<Popconfirm
title="确认删除?"
okText="是"
cancelText="否"
onConfirm={async () => {
const { code, msg } = await deleteMetric(record.id);
if (code === 200) {
setMetricItem(undefined);
actionRef.current?.reload();
} else {
message.error(msg);
}
}}
>
<a
key="metricDeleteBtn"
onClick={() => {
setMetricItem(record);
}}
>
</a>
</Popconfirm>
</Space>
);
},
},
]; ];
const handleFilterChange = async (filterParams: { const handleFilterChange = async (filterParams: {
@@ -179,6 +227,26 @@ const ClassMetricTable: React.FC<Props> = () => {
size="small" size="small"
options={{ reload: false, density: false, fullScreen: false }} options={{ reload: false, density: false, fullScreen: false }}
/> />
{createModalVisible && (
<MetricInfoCreateForm
domainId={Number(selectDomainId)}
createModalVisible={createModalVisible}
metricItem={metricItem}
onSubmit={() => {
setCreateModalVisible(false);
actionRef?.current?.reload();
dispatch({
type: 'domainManger/queryMetricList',
payload: {
domainId: selectDomainId,
},
});
}}
onCancel={() => {
setCreateModalVisible(false);
}}
/>
)}
</> </>
); );
}; };

View File

@@ -0,0 +1,290 @@
import { Popover, message, Space } from 'antd';
import React, { useEffect, useState } from 'react';
import { connect, Helmet, history, useParams, useRouteMatch, useLocation } from 'umi';
import DomainListTree from './components/DomainList';
import styles from './components/style.less';
import type { StateType } from './model';
import { DownOutlined } from '@ant-design/icons';
import { ISemantic } from './data';
import { getDomainList, getModelList } from './service';
import ChatSettingTab from './ChatSetting/ChatSettingTab';
import DomainManagerTab from './components/DomainManagerTab';
import type { Dispatch } from 'umi';
type Props = {
mode: 'domain' | 'chatSetting';
domainManger: StateType;
dispatch: Dispatch;
};
const OverviewContainer: React.FC<Props> = ({ mode, domainManger, dispatch }) => {
const params: any = useParams();
const domainId = params.domainId;
const modelId = params.modelId;
const menuKey = params.menuKey ? params.menuKey : !Number(modelId) ? 'overview' : '';
const { selectDomainId, selectModelId, selectDomainName, selectModelName, domainList } =
domainManger;
const [modelList, setModelList] = useState<ISemantic.IDomainItem[]>([]);
const [isModel, setIsModel] = useState<boolean>(false);
const [open, setOpen] = useState(false);
const [activeKey, setActiveKey] = useState<string>(menuKey);
const handleOpenChange = (newOpen: boolean) => {
setOpen(newOpen);
};
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, name } = firstRootNode;
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: id,
selectDomainName: name,
domainData: firstRootNode,
});
setActiveKey(menuKey);
pushUrlMenu(id, 0, menuKey);
}
} else {
const { id, name } = targetNode;
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: id,
selectDomainName: name,
domainData: targetNode,
});
}
};
const initProjectTree = async () => {
const { code, data, msg } = await getDomainList();
if (code === 200) {
initSelectedDomain(data);
dispatch({
type: 'domainManger/setDomainList',
payload: { domainList: data },
});
} else {
message.error(msg);
}
};
useEffect(() => {
initProjectTree();
}, []);
useEffect(() => {
if (!selectDomainId) {
return;
}
queryModelList();
dispatch({
type: 'domainManger/queryDatabaseByDomainId',
payload: {
domainId: selectDomainId,
},
});
}, [selectDomainId]);
const queryModelList = async () => {
const { code, data } = await getModelList(selectDomainId);
if (code === 200) {
setModelList(data);
const model = data.filter((item: any) => {
return `${item.id}` === modelId;
})[0];
if (model) {
const { id, name } = model;
dispatch({
type: 'domainManger/setSelectModel',
selectModelId: id,
selectModelName: name,
modelData: model,
});
setActiveKey(menuKey);
setIsModel(true);
pushUrlMenu(selectDomainId, id, menuKey);
}
} else {
message.error('获取模型列表失败!');
}
};
useEffect(() => {
if (!selectDomainId) {
return;
}
setIsModel(false);
setActiveKey(menuKey);
}, [domainList, selectDomainId]);
const initModelConfig = () => {
setIsModel(true);
const currentMenuKey = menuKey === 'overview' ? '' : menuKey;
pushUrlMenu(selectDomainId, selectModelId, currentMenuKey);
setActiveKey(currentMenuKey);
};
useEffect(() => {
if (!selectModelId) {
return;
}
initModelConfig();
dispatch({
type: 'domainManger/queryDimensionList',
payload: {
modelId: selectModelId,
},
});
dispatch({
type: 'domainManger/queryMetricList',
payload: {
modelId: selectModelId,
},
});
}, [selectModelId]);
const pushUrlMenu = (domainId: number, modelId: number, menuKey: string) => {
const path = mode === 'domain' ? 'semanticModel' : 'chatSetting';
history.push(`/${path}/model/${domainId}/${modelId || 0}/${menuKey}`);
};
const handleModelChange = (model?: ISemantic.IModelItem) => {
queryModelList();
if (!model) {
return;
}
if (`${model.id}` === `${selectModelId}`) {
initModelConfig();
}
const { id, name } = model;
dispatch({
type: 'domainManger/setSelectModel',
selectModelId: id,
selectModelName: name,
modelData: model,
});
};
const cleanModelInfo = (domainId: number) => {
setIsModel(false);
pushUrlMenu(domainId, 0, 'overview');
setActiveKey('overview');
dispatch({
type: 'domainManger/setSelectModel',
selectModelId: 0,
selectModelName: '',
modelData: undefined,
});
};
return (
<div className={styles.projectBody}>
<Helmet title={'模型管理-超音数'} />
<div className={styles.projectManger}>
<h2 className={styles.title}>
<Popover
zIndex={1000}
overlayInnerStyle={{
overflow: 'scroll',
maxHeight: '800px',
}}
content={
<DomainListTree
createDomainBtnVisible={mode === 'domain' ? true : false}
onTreeSelected={(domainData) => {
setOpen(false);
const { id, name } = domainData;
cleanModelInfo(id);
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: id,
selectDomainName: name,
domainData,
});
}}
onTreeDataUpdate={() => {
initProjectTree();
}}
/>
}
trigger="click"
open={open}
onOpenChange={handleOpenChange}
>
<div className={styles.domainSelector}>
<span className={styles.domainTitle}>
<Space>
{selectDomainName ? `当前主题域:${selectDomainName}` : '主题域信息'}
{selectModelName && (
<>
<span style={{ position: 'relative', top: '-2px' }}> | </span>
<span style={{ fontSize: 16, color: '#296DF3' }}>{selectModelName}</span>
</>
)}
</Space>
</span>
<span className={styles.downIcon}>
<DownOutlined />
</span>
</div>
</Popover>
</h2>
{selectDomainId ? (
<>
{mode === 'domain' ? (
<DomainManagerTab
isModel={isModel}
activeKey={activeKey}
modelList={modelList}
handleModelChange={(model) => {
handleModelChange(model);
}}
onBackDomainBtnClick={() => {
cleanModelInfo(selectDomainId);
}}
onMenuChange={(menuKey) => {
setActiveKey(menuKey);
pushUrlMenu(selectDomainId, selectModelId, menuKey);
}}
/>
) : (
<ChatSettingTab
isModel={isModel}
activeKey={activeKey}
modelList={modelList}
handleModelChange={(model) => {
handleModelChange(model);
}}
onBackDomainBtnClick={() => {
cleanModelInfo(selectDomainId);
}}
onMenuChange={(menuKey) => {
setActiveKey(menuKey);
pushUrlMenu(selectDomainId, selectModelId, menuKey);
}}
/>
)}
</>
) : (
<h2 className={styles.mainTip}></h2>
)}
</div>
</div>
);
};
export default connect(({ domainManger }: { domainManger: StateType }) => ({
domainManger,
}))(OverviewContainer);

View File

@@ -1,251 +0,0 @@
import { Tabs, Popover, message } from 'antd';
import React, { useEffect, useState } from 'react';
import { connect, Helmet, history, useParams } from 'umi';
import DomainListTree from './components/DomainList';
import ClassDataSourceTable from './components/ClassDataSourceTable';
import ClassDimensionTable from './components/ClassDimensionTable';
import ClassMetricTable from './components/ClassMetricTable';
import PermissionSection from './components/Permission/PermissionSection';
import DatabaseSection from './components/Database/DatabaseSection';
import EntitySettingSection from './components/Entity/EntitySettingSection';
import OverView from './components/OverView';
import styles from './components/style.less';
import type { StateType } from './model';
import { DownOutlined } from '@ant-design/icons';
import { ISemantic } from './data';
import { findLeafNodesFromDomainList } from './utils';
import SemanticGraphCanvas from './SemanticGraphCanvas';
import { getDomainList } from './service';
import type { Dispatch } from 'umi';
type Props = {
domainManger: StateType;
dispatch: Dispatch;
};
const DomainManger: React.FC<Props> = ({ domainManger, dispatch }) => {
const defaultTabKey = 'xflow';
const params: any = useParams();
const menuKey = params.menuKey ? params.menuKey : defaultTabKey;
const modelId = params.modelId;
const { selectDomainId, selectDomainName, domainList } = domainManger;
const [modelList, setModelList] = useState<ISemantic.IDomainItem[]>([]);
const [isModel, setIsModel] = useState<boolean>(false);
const [open, setOpen] = useState(false);
const [activeKey, setActiveKey] = useState<string>(menuKey);
useEffect(() => {
setActiveKey(menuKey);
}, [menuKey]);
const initSelectedDomain = (domainList: ISemantic.IDomainItem[]) => {
const targetNode = domainList.filter((item: any) => {
return `${item.id}` === modelId;
})[0];
if (!targetNode) {
const firstRootNode = domainList.filter((item: any) => {
return item.parentId === 0;
})[0];
if (firstRootNode) {
const { id, name } = firstRootNode;
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: id,
selectDomainName: name,
domainData: firstRootNode,
});
}
} else {
const { id, name } = targetNode;
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: id,
selectDomainName: name,
domainData: targetNode,
});
}
};
const initProjectTree = async () => {
const { code, data, msg } = await getDomainList();
if (code === 200) {
if (!selectDomainId) {
initSelectedDomain(data);
}
dispatch({
type: 'domainManger/setDomainList',
payload: { domainList: data },
});
} else {
message.error(msg);
}
};
useEffect(() => {
initProjectTree();
}, []);
useEffect(() => {
if (!selectDomainId) {
return;
}
const list = findLeafNodesFromDomainList(domainList, selectDomainId);
setModelList(list);
if (Array.isArray(list) && list.length > 0) {
setIsModel(false);
pushUrlMenu(selectDomainId, 'overview');
setActiveKey('overview');
} else {
setIsModel(true);
const currentMenuKey = menuKey === 'overview' ? defaultTabKey : menuKey;
pushUrlMenu(selectDomainId, currentMenuKey);
setActiveKey(currentMenuKey);
}
}, [domainList, selectDomainId]);
const handleOpenChange = (newOpen: boolean) => {
setOpen(newOpen);
};
const pushUrlMenu = (domainId: number, menuKey: string) => {
history.push(`/semanticModel/${domainId}/${menuKey}`);
};
useEffect(() => {
if (selectDomainId) {
dispatch({
type: 'domainManger/queryDimensionList',
payload: {
domainId: selectDomainId,
},
});
dispatch({
type: 'domainManger/queryMetricList',
payload: {
domainId: selectDomainId,
},
});
dispatch({
type: 'domainManger/queryDatabaseByDomainId',
payload: {
domainId: selectDomainId,
},
});
}
}, [selectDomainId]);
const tabItem = [
{
label: '子主题域',
key: 'overview',
children: <OverView modelList={modelList} />,
},
{
label: '权限管理',
key: 'permissonSetting',
children: <PermissionSection />,
},
];
const isModelItem = [
{
label: '画布',
key: 'xflow',
children: (
<div style={{ width: '100%', marginTop: -20 }}>
<SemanticGraphCanvas />
</div>
),
},
{
label: '数据库',
key: 'dataBase',
children: <DatabaseSection />,
},
{
label: '数据源',
key: 'dataSource',
children: <ClassDataSourceTable />,
},
{
label: '维度',
key: 'dimenstion',
children: <ClassDimensionTable key={selectDomainId} />,
},
{
label: '指标',
key: 'metric',
children: <ClassMetricTable />,
},
{
label: '实体',
key: 'entity',
children: <EntitySettingSection />,
},
{
label: '权限管理',
key: 'permissonSetting',
children: <PermissionSection />,
},
];
return (
<div className={styles.projectBody}>
<Helmet title={'模型管理-超音数'} />
<div className={styles.projectManger}>
<h2 className={styles.title}>
<Popover
zIndex={1000}
overlayInnerStyle={{
overflow: 'scroll',
maxHeight: '800px',
}}
content={
<DomainListTree
onTreeSelected={() => {
setOpen(false);
}}
onTreeDataUpdate={() => {
initProjectTree();
}}
/>
}
trigger="click"
open={open}
onOpenChange={handleOpenChange}
>
<div className={styles.domainSelector}>
<span className={styles.domainTitle}>
{selectDomainName ? `当前主题域:${selectDomainName}` : '主题域信息'}
</span>
<span className={styles.downIcon}>
<DownOutlined />
</span>
</div>
</Popover>
</h2>
{selectDomainId ? (
<>
<Tabs
className={styles.tab}
items={!isModel ? tabItem : isModelItem}
activeKey={activeKey}
destroyInactiveTabPane
onChange={(menuKey: string) => {
setActiveKey(menuKey);
pushUrlMenu(selectDomainId, menuKey);
}}
/>
</>
) : (
<h2 className={styles.mainTip}></h2>
)}
</div>
</div>
);
};
export default connect(({ domainManger }: { domainManger: StateType }) => ({
domainManger,
}))(DomainManger);

View File

@@ -13,7 +13,6 @@ import {
deleteDatasource, deleteDatasource,
getDimensionList, getDimensionList,
createOrUpdateViewInfo, createOrUpdateViewInfo,
getViewInfoList,
deleteDatasourceRela, deleteDatasourceRela,
} from '../service'; } from '../service';
import { message } from 'antd'; import { message } from 'antd';
@@ -85,8 +84,8 @@ export namespace GraphApi {
export const loadDataSourceData = async (args: NsGraph.IGraphMeta) => { export const loadDataSourceData = async (args: NsGraph.IGraphMeta) => {
const { domainManger, graphConfig } = args.meta; const { domainManger, graphConfig } = args.meta;
const { selectDomainId } = domainManger; const { selectModelId } = domainManger;
const { code, data = [] } = await getDatasourceList({ domainId: selectDomainId }); const { code, data = [] } = await getDatasourceList({ modelId: selectModelId });
const dataSourceMap = data.reduce( const dataSourceMap = data.reduce(
(itemMap: Record<string, IDataSource.IDataSourceItem>, item: IDataSource.IDataSourceItem) => { (itemMap: Record<string, IDataSource.IDataSourceItem>, item: IDataSource.IDataSourceItem) => {
const { id, name } = item; const { id, name } = item;
@@ -161,8 +160,8 @@ export namespace GraphApi {
export const loadDimensionData = async (args: NsGraph.IGraphMeta) => { export const loadDimensionData = async (args: NsGraph.IGraphMeta) => {
const { domainManger } = args.meta; const { domainManger } = args.meta;
const { domainId } = domainManger; const { selectModelId } = domainManger;
const { code, data } = await getDimensionList({ domainId }); const { code, data } = await getDimensionList({ modelId: selectModelId });
if (code === 200) { if (code === 200) {
const { list } = data; const { list } = data;
const nodes: NsGraph.INodeConfig[] = list.map((item: any) => { const nodes: NsGraph.INodeConfig[] = list.map((item: any) => {
@@ -210,7 +209,7 @@ export namespace GraphApi {
const { domainManger, graphConfig } = graphMeta.meta; const { domainManger, graphConfig } = graphMeta.meta;
const { code, msg } = await createOrUpdateViewInfo({ const { code, msg } = await createOrUpdateViewInfo({
id: graphConfig?.id, id: graphConfig?.id,
domainId: domainManger.selectDomainId, modelId: domainManger.selectModelId,
type: 'datasource', type: 'datasource',
config: JSON.stringify(tempGraphData), config: JSON.stringify(tempGraphData),
}); });

View File

@@ -18,7 +18,6 @@ const DeleteConfirmModal: React.FC<Props> = ({
}) => { }) => {
const [confirmLoading, setConfirmLoading] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false);
const deleteNode = async () => { const deleteNode = async () => {
setConfirmLoading(true);
const { id, nodeType } = nodeData; const { id, nodeType } = nodeData;
let deleteQuery; let deleteQuery;
if (nodeType === SemanticNodeType.DIMENSION) { if (nodeType === SemanticNodeType.DIMENSION) {
@@ -34,6 +33,7 @@ const DeleteConfirmModal: React.FC<Props> = ({
message.error('当前节点类型不是维度,指标,数据源中的一种,请确认节点数据'); message.error('当前节点类型不是维度,指标,数据源中的一种,请确认节点数据');
return; return;
} }
setConfirmLoading(true);
const { code, msg } = await deleteQuery(id); const { code, msg } = await deleteQuery(id);
setConfirmLoading(false); setConfirmLoading(false);
if (code === 200) { if (code === 200) {

View File

@@ -32,7 +32,6 @@ import GraphLegendVisibleModeItem from './components/GraphLegendVisibleModeItem'
// import { cloneDeep } from 'lodash'; // import { cloneDeep } from 'lodash';
type Props = { type Props = {
domainId: number;
// graphShowType?: SemanticNodeType; // graphShowType?: SemanticNodeType;
domainManger: StateType; domainManger: StateType;
dispatch: Dispatch; dispatch: Dispatch;
@@ -40,7 +39,6 @@ type Props = {
const DomainManger: React.FC<Props> = ({ const DomainManger: React.FC<Props> = ({
domainManger, domainManger,
domainId,
// graphShowType = SemanticNodeType.DIMENSION, // graphShowType = SemanticNodeType.DIMENSION,
// graphShowType, // graphShowType,
dispatch, dispatch,
@@ -65,7 +63,7 @@ const DomainManger: React.FC<Props> = ({
const [dataSourceInfoList, setDataSourceInfoList] = useState<IDataSource.IDataSourceItem[]>([]); const [dataSourceInfoList, setDataSourceInfoList] = useState<IDataSource.IDataSourceItem[]>([]);
const { dimensionList, metricList } = domainManger; const { dimensionList, metricList, selectModelId: modelId, selectDomainId } = domainManger;
const dimensionListRef = useRef<ISemantic.IDimensionItem[]>([]); const dimensionListRef = useRef<ISemantic.IDimensionItem[]>([]);
const metricListRef = useRef<ISemantic.IMetricItem[]>([]); const metricListRef = useRef<ISemantic.IMetricItem[]>([]);
@@ -139,10 +137,10 @@ const DomainManger: React.FC<Props> = ({
}; };
const queryDataSourceList = async (params: { const queryDataSourceList = async (params: {
domainId: number; modelId: number;
graphShowType?: SemanticNodeType; graphShowType?: SemanticNodeType;
}) => { }) => {
const { code, data } = await getDomainSchemaRela(params.domainId); const { code, data } = await getDomainSchemaRela(params.modelId);
if (code === 200) { if (code === 200) {
if (data) { if (data) {
setDataSourceInfoList( setDataSourceInfoList(
@@ -165,8 +163,8 @@ const DomainManger: React.FC<Props> = ({
useEffect(() => { useEffect(() => {
graphLegendDataSourceIds.current = undefined; graphLegendDataSourceIds.current = undefined;
graphRef.current = null; graphRef.current = null;
queryDataSourceList({ domainId }); queryDataSourceList({ modelId });
}, [domainId]); }, [modelId]);
// const getLegendDataFilterFunctions = () => { // const getLegendDataFilterFunctions = () => {
// legendDataRef.current.map((item: any) => { // legendDataRef.current.map((item: any) => {
@@ -273,7 +271,7 @@ const DomainManger: React.FC<Props> = ({
if (targetData.nodeType === SemanticNodeType.DIMENSION) { if (targetData.nodeType === SemanticNodeType.DIMENSION) {
const targetItem = dimensionListRef.current.find((item) => item.id === targetData.uid); const targetItem = dimensionListRef.current.find((item) => item.id === targetData.uid);
if (targetItem) { if (targetItem) {
setCurrentNodeData(targetItem); setCurrentNodeData({ ...targetData, ...targetItem });
setConfirmModalOpenState(true); setConfirmModalOpenState(true);
} else { } else {
message.error('获取维度初始化数据失败'); message.error('获取维度初始化数据失败');
@@ -282,7 +280,7 @@ const DomainManger: React.FC<Props> = ({
if (targetData.nodeType === SemanticNodeType.METRIC) { if (targetData.nodeType === SemanticNodeType.METRIC) {
const targetItem = metricListRef.current.find((item) => item.id === targetData.uid); const targetItem = metricListRef.current.find((item) => item.id === targetData.uid);
if (targetItem) { if (targetItem) {
setCurrentNodeData(targetItem); setCurrentNodeData({ ...targetData, ...targetItem });
setConfirmModalOpenState(true); setConfirmModalOpenState(true);
} else { } else {
message.error('获取指标初始化数据失败'); message.error('获取指标初始化数据失败');
@@ -515,7 +513,7 @@ const DomainManger: React.FC<Props> = ({
const updateGraphData = async (params?: { graphShowType?: SemanticNodeType }) => { const updateGraphData = async (params?: { graphShowType?: SemanticNodeType }) => {
const graphRootData = await queryDataSourceList({ const graphRootData = await queryDataSourceList({
domainId, modelId,
graphShowType: params?.graphShowType, graphShowType: params?.graphShowType,
}); });
if (graphRootData) { if (graphRootData) {
@@ -571,7 +569,7 @@ const DomainManger: React.FC<Props> = ({
/> />
<div <div
ref={ref} ref={ref}
key={`${domainId}`} key={`${modelId}`}
id="semanticGraph" id="semanticGraph"
style={{ width: '100%', height: 'calc(100vh - 175px)', position: 'relative' }} style={{ width: '100%', height: 'calc(100vh - 175px)', position: 'relative' }}
/> />
@@ -595,7 +593,7 @@ const DomainManger: React.FC<Props> = ({
dispatch({ dispatch({
type: 'domainManger/queryMetricList', type: 'domainManger/queryMetricList',
payload: { payload: {
domainId, modelId,
}, },
}); });
} }
@@ -603,7 +601,7 @@ const DomainManger: React.FC<Props> = ({
dispatch({ dispatch({
type: 'domainManger/queryDimensionList', type: 'domainManger/queryDimensionList',
payload: { payload: {
domainId, modelId,
}, },
}); });
} }
@@ -612,7 +610,7 @@ const DomainManger: React.FC<Props> = ({
{createDimensionModalVisible && ( {createDimensionModalVisible && (
<DimensionInfoModal <DimensionInfoModal
domainId={domainId} modelId={modelId}
bindModalVisible={createDimensionModalVisible} bindModalVisible={createDimensionModalVisible}
dimensionItem={dimensionItem} dimensionItem={dimensionItem}
dataSourceList={nodeDataSource ? [nodeDataSource] : dataSourceInfoList} dataSourceList={nodeDataSource ? [nodeDataSource] : dataSourceInfoList}
@@ -622,7 +620,7 @@ const DomainManger: React.FC<Props> = ({
dispatch({ dispatch({
type: 'domainManger/queryDimensionList', type: 'domainManger/queryDimensionList',
payload: { payload: {
domainId, modelId,
}, },
}); });
}} }}
@@ -633,7 +631,8 @@ const DomainManger: React.FC<Props> = ({
)} )}
{createMetricModalVisible && ( {createMetricModalVisible && (
<MetricInfoCreateForm <MetricInfoCreateForm
domainId={domainId} domainId={selectDomainId}
modelId={modelId}
key={metricItem?.id} key={metricItem?.id}
datasourceId={nodeDataSource?.id} datasourceId={nodeDataSource?.id}
createModalVisible={createMetricModalVisible} createModalVisible={createMetricModalVisible}
@@ -644,7 +643,7 @@ const DomainManger: React.FC<Props> = ({
dispatch({ dispatch({
type: 'domainManger/queryMetricList', type: 'domainManger/queryMetricList',
payload: { payload: {
domainId, modelId,
}, },
}); });
}} }}
@@ -676,13 +675,13 @@ const DomainManger: React.FC<Props> = ({
? dispatch({ ? dispatch({
type: 'domainManger/queryDimensionList', type: 'domainManger/queryDimensionList',
payload: { payload: {
domainId, modelId,
}, },
}) })
: dispatch({ : dispatch({
type: 'domainManger/queryMetricList', type: 'domainManger/queryMetricList',
payload: { payload: {
domainId, modelId,
}, },
}); });
}} }}

View File

@@ -38,7 +38,7 @@ const SemanticGraphCanvas: React.FC<Props> = ({ domainManger }) => {
</div> </div>
) : ( */} ) : ( */}
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
<SemanticGraph domainId={selectDomainId} /> <SemanticGraph />
</div> </div>
{/* )} */} {/* )} */}
</div> </div>

View File

@@ -113,6 +113,7 @@ const BindMeasuresTable: React.FC<CreateFormProps> = ({
size="small" size="small"
search={false} search={false}
options={false} options={false}
scroll={{ y: 800 }}
/> />
</Modal> </Modal>
); );

View File

@@ -15,7 +15,7 @@ type Props = {
}; };
const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => { const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
const { selectDomainId } = domainManger; const { selectModelId } = domainManger;
const [dataSourceItem, setDataSourceItem] = useState<any>(); const [dataSourceItem, setDataSourceItem] = useState<any>();
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false); const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
@@ -59,7 +59,7 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
return ( return (
<Space> <Space>
<a <a
key="classEditBtn" key="datasourceEditBtn"
onClick={() => { onClick={() => {
setDataSourceItem(record); setDataSourceItem(record);
setCreateDataSourceModalOpen(true); setCreateDataSourceModalOpen(true);
@@ -82,7 +82,7 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
}} }}
> >
<a <a
key="classEditBtn" key="datasourceDeleteBtn"
onClick={() => { onClick={() => {
setDataSourceItem(record); setDataSourceItem(record);
}} }}
@@ -127,7 +127,7 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
actionRef={actionRef} actionRef={actionRef}
rowKey="id" rowKey="id"
columns={columns} columns={columns}
params={{ domainId: selectDomainId }} params={{ modelId: selectModelId }}
request={queryDataSourceList} request={queryDataSourceList}
pagination={false} pagination={false}
search={false} search={false}

View File

@@ -28,7 +28,7 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
onCancel, onCancel,
dispatch, dispatch,
}) => { }) => {
const { selectDomainId, dataBaseConfig } = domainManger; const { selectDomainId, dataBaseConfig, selectModelId } = domainManger;
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false); const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false); const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false);
@@ -128,7 +128,7 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
type="primary" type="primary"
key="console" key="console"
onClick={() => { onClick={() => {
history.replace(`/semanticModel/${selectDomainId}/dataBase`); history.replace(`/semanticModel/${selectDomainId}/0/dataBase`);
onCancel?.(); onCancel?.();
}} }}
> >
@@ -173,7 +173,6 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
> >
<DataSource <DataSource
initialValues={dataSourceItem} initialValues={dataSourceItem}
domainId={Number(selectDomainId)}
onSubmitSuccess={() => { onSubmitSuccess={() => {
setCreateModalVisible(false); setCreateModalVisible(false);
onSubmit?.(); onSubmit?.();

View File

@@ -1,101 +0,0 @@
import { Modal, Card, Row, Col, Result, Button } from 'antd';
import { ConsoleSqlOutlined, CoffeeOutlined } from '@ant-design/icons';
import React, { useState, useEffect } from 'react';
import { history, connect } from 'umi';
import type { StateType } from '../model';
const { Meta } = Card;
type Props = {
open: boolean;
domainManger: StateType;
onTypeChange: (type: 'fast' | 'normal') => void;
onCancel?: () => void;
};
const ClassDataSourceTypeModal: React.FC<Props> = ({
open,
onTypeChange,
domainManger,
onCancel,
}) => {
const { selectDomainId, dataBaseConfig } = domainManger;
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
useEffect(() => {
setCreateDataSourceModalOpen(open);
}, [open]);
return (
<>
<Modal
open={createDataSourceModalOpen}
onCancel={() => {
setCreateDataSourceModalOpen(false);
onCancel?.();
}}
footer={null}
centered
closable={false}
>
{dataBaseConfig && dataBaseConfig.id ? (
<Row gutter={16} style={{ marginTop: '0px' }}>
<Col span={12}>
<Card
hoverable
style={{ height: 220 }}
onClick={() => {
onTypeChange('fast');
setCreateDataSourceModalOpen(false);
}}
cover={
<CoffeeOutlined
width={240}
style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
/>
}
>
<Meta title="快速创建" description="自动进行数据源可视化创建" />
</Card>
</Col>
<Col span={12}>
<Card
onClick={() => {
onTypeChange('normal');
setCreateDataSourceModalOpen(false);
}}
hoverable
style={{ height: 220 }}
cover={
<ConsoleSqlOutlined
style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
/>
}
>
<Meta title="SQL脚本" description="自定义SQL脚本创建数据源" />
</Card>
</Col>
</Row>
) : (
<Result
status="warning"
subTitle="创建数据源需要先完成数据库设置"
extra={
<Button
type="primary"
key="console"
onClick={() => {
history.replace(`/semanticModel/${selectDomainId}/dataBase`);
onCancel?.();
}}
>
</Button>
}
/>
)}
</Modal>
</>
);
};
export default connect(({ domainManger }: { domainManger: StateType }) => ({
domainManger,
}))(ClassDataSourceTypeModal);

View File

@@ -19,7 +19,7 @@ type Props = {
}; };
const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => { const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
const { selectDomainId } = domainManger; const { selectModelId: modelId } = domainManger;
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false); const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const [dimensionItem, setDimensionItem] = useState<ISemantic.IDimensionItem>(); const [dimensionItem, setDimensionItem] = useState<ISemantic.IDimensionItem>();
const [dataSourceList, setDataSourceList] = useState<any[]>([]); const [dataSourceList, setDataSourceList] = useState<any[]>([]);
@@ -40,7 +40,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
const { code, data, msg } = await getDimensionList({ const { code, data, msg } = await getDimensionList({
...params, ...params,
...pagination, ...pagination,
domainId: selectDomainId, modelId,
}); });
const { list, pageSize, current, total } = data || {}; const { list, pageSize, current, total } = data || {};
let resData: any = {}; let resData: any = {};
@@ -67,7 +67,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
}; };
const queryDataSourceList = async () => { const queryDataSourceList = async () => {
const { code, data, msg } = await getDatasourceList({ domainId: selectDomainId }); const { code, data, msg } = await getDatasourceList({ modelId });
if (code === 200) { if (code === 200) {
setDataSourceList(data); setDataSourceList(data);
} else { } else {
@@ -77,7 +77,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
useEffect(() => { useEffect(() => {
queryDataSourceList(); queryDataSourceList();
}, [selectDomainId]); }, [modelId]);
const columns: ProColumns[] = [ const columns: ProColumns[] = [
{ {
@@ -139,7 +139,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
return ( return (
<Space> <Space>
<a <a
key="classEditBtn" key="dimensionEditBtn"
onClick={() => { onClick={() => {
setDimensionItem(record); setDimensionItem(record);
setCreateModalVisible(true); setCreateModalVisible(true);
@@ -148,7 +148,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
</a> </a>
<a <a
key="classEditBtn" key="dimensionValueEditBtn"
onClick={() => { onClick={() => {
setDimensionItem(record); setDimensionItem(record);
setDimensionValueSettingModalVisible(true); setDimensionValueSettingModalVisible(true);
@@ -176,7 +176,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
}} }}
> >
<a <a
key="classEditBtn" key="dimensionDeleteEditBtn"
onClick={() => { onClick={() => {
setDimensionItem(record); setDimensionItem(record);
}} }}
@@ -195,7 +195,6 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
<ProTable <ProTable
className={`${styles.classTable} ${styles.classTableSelectColumnAlignLeft}`} className={`${styles.classTable} ${styles.classTableSelectColumnAlignLeft}`}
actionRef={actionRef} actionRef={actionRef}
// headerTitle="维度列表"
rowKey="id" rowKey="id"
columns={columns} columns={columns}
request={queryDimensionList} request={queryDimensionList}
@@ -236,7 +235,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
{createModalVisible && ( {createModalVisible && (
<DimensionInfoModal <DimensionInfoModal
domainId={selectDomainId} modelId={modelId}
bindModalVisible={createModalVisible} bindModalVisible={createModalVisible}
dimensionItem={dimensionItem} dimensionItem={dimensionItem}
dataSourceList={dataSourceList} dataSourceList={dataSourceList}
@@ -246,7 +245,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
dispatch({ dispatch({
type: 'domainManger/queryDimensionList', type: 'domainManger/queryDimensionList',
payload: { payload: {
domainId: selectDomainId, modelId,
}, },
}); });
return; return;
@@ -269,7 +268,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
dispatch({ dispatch({
type: 'domainManger/queryDimensionList', type: 'domainManger/queryDimensionList',
payload: { payload: {
domainId: selectDomainId, modelId,
}, },
}); });
setDimensionValueSettingModalVisible(false); setDimensionValueSettingModalVisible(false);

View File

@@ -12,6 +12,7 @@ import MetricInfoCreateForm from './MetricInfoCreateForm';
import moment from 'moment'; import moment from 'moment';
import styles from './style.less'; import styles from './style.less';
import { ISemantic } from '../data';
type Props = { type Props = {
dispatch: Dispatch; dispatch: Dispatch;
@@ -19,9 +20,9 @@ type Props = {
}; };
const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => { const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
const { selectDomainId } = domainManger; const { selectModelId: modelId, selectDomainId } = domainManger;
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false); const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const [metricItem, setMetricItem] = useState<any>(); const [metricItem, setMetricItem] = useState<ISemantic.IMetricItem>();
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState({
current: 1, current: 1,
pageSize: 20, pageSize: 20,
@@ -33,7 +34,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
const { code, data, msg } = await queryMetric({ const { code, data, msg } = await queryMetric({
...params, ...params,
...pagination, ...pagination,
domainId: selectDomainId, modelId,
}); });
const { list, pageSize, current, total } = data || {}; const { list, pageSize, current, total } = data || {};
let resData: any = {}; let resData: any = {};
@@ -95,7 +96,6 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
{ {
dataIndex: 'type', dataIndex: 'type',
title: '指标类型', title: '指标类型',
// search: false,
valueEnum: { valueEnum: {
ATOMIC: '原子指标', ATOMIC: '原子指标',
DERIVED: '衍生指标', DERIVED: '衍生指标',
@@ -128,7 +128,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
return ( return (
<Space> <Space>
<a <a
key="classEditBtn" key="metricEditBtn"
onClick={() => { onClick={() => {
setMetricItem(record); setMetricItem(record);
setCreateModalVisible(true); setCreateModalVisible(true);
@@ -152,7 +152,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
}} }}
> >
<a <a
key="classEditBtn" key="metricDeleteBtn"
onClick={() => { onClick={() => {
setMetricItem(record); setMetricItem(record);
}} }}
@@ -171,7 +171,6 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
<ProTable <ProTable
className={`${styles.classTable} ${styles.classTableSelectColumnAlignLeft}`} className={`${styles.classTable} ${styles.classTableSelectColumnAlignLeft}`}
actionRef={actionRef} actionRef={actionRef}
// headerTitle="指标列表"
rowKey="id" rowKey="id"
search={{ search={{
span: 4, span: 4,
@@ -181,7 +180,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
}, },
}} }}
columns={columns} columns={columns}
params={{ domainId: selectDomainId }} params={{ modelId }}
request={queryMetricList} request={queryMetricList}
pagination={pagination} pagination={pagination}
tableAlertRender={() => { tableAlertRender={() => {
@@ -212,7 +211,8 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
/> />
{createModalVisible && ( {createModalVisible && (
<MetricInfoCreateForm <MetricInfoCreateForm
domainId={Number(selectDomainId)} domainId={selectDomainId}
modelId={Number(modelId)}
createModalVisible={createModalVisible} createModalVisible={createModalVisible}
metricItem={metricItem} metricItem={metricItem}
onSubmit={() => { onSubmit={() => {
@@ -221,7 +221,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
dispatch({ dispatch({
type: 'domainManger/queryMetricList', type: 'domainManger/queryMetricList',
payload: { payload: {
domainId: selectDomainId, modelId,
}, },
}); });
}} }}

View File

@@ -1,7 +1,7 @@
import { useEffect, forwardRef, useImperativeHandle, useState } from 'react'; import { useEffect, forwardRef, useImperativeHandle, useState } from 'react';
import type { ForwardRefRenderFunction } from 'react'; import type { ForwardRefRenderFunction } from 'react';
import { message, Form, Input, Select, Button, Space } from 'antd'; import { message, Form, Input, Select, Button, Space } from 'antd';
import { saveDatabase, getDatabaseByDomainId, testDatabaseConnect } from '../../service'; import { saveDatabase, testDatabaseConnect } from '../../service';
import { formLayout } from '@/components/FormHelper/utils'; import { formLayout } from '@/components/FormHelper/utils';
import styles from '../style.less'; import styles from '../style.less';

View File

@@ -10,7 +10,7 @@ import { createDimension, updateDimension } from '../service';
import { message } from 'antd'; import { message } from 'antd';
export type CreateFormProps = { export type CreateFormProps = {
domainId: number; modelId: number;
dimensionItem?: ISemantic.IDimensionItem; dimensionItem?: ISemantic.IDimensionItem;
onCancel: () => void; onCancel: () => void;
bindModalVisible: boolean; bindModalVisible: boolean;
@@ -24,7 +24,7 @@ const { Option } = Select;
const { TextArea } = Input; const { TextArea } = Input;
const DimensionInfoModal: React.FC<CreateFormProps> = ({ const DimensionInfoModal: React.FC<CreateFormProps> = ({
domainId, modelId,
onCancel, onCancel,
bindModalVisible, bindModalVisible,
dimensionItem, dimensionItem,
@@ -55,7 +55,7 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
const saveDimension = async (fieldsValue: any, isSilenceSubmit = false) => { const saveDimension = async (fieldsValue: any, isSilenceSubmit = false) => {
const queryParams = { const queryParams = {
domainId, modelId,
type: 'categorical', type: 'categorical',
...fieldsValue, ...fieldsValue,
}; };

View File

@@ -24,7 +24,7 @@ type DomainListProps = {
createDomainBtnVisible?: boolean; createDomainBtnVisible?: boolean;
dispatch: Dispatch; dispatch: Dispatch;
onCreateDomainBtnClick?: () => void; onCreateDomainBtnClick?: () => void;
onTreeSelected?: () => void; onTreeSelected?: (targetNodeData: ISemantic.IDomainItem) => void;
onTreeDataUpdate?: () => void; onTreeDataUpdate?: () => void;
}; };
@@ -57,7 +57,7 @@ const DomainListTree: FC<DomainListProps> = ({
const [projectInfoParams, setProjectInfoParams] = useState<any>({}); const [projectInfoParams, setProjectInfoParams] = useState<any>({});
const [filterValue, setFliterValue] = useState<string>(''); const [filterValue, setFliterValue] = useState<string>('');
const [expandedKeys, setExpandedKeys] = useState<string[]>([]); const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
const [classList, setClassList] = useState<any[]>([]); const [classList, setClassList] = useState<ISemantic.IDomainItem[]>([]);
useEffect(() => { useEffect(() => {
const treeData = addPathInTreeData(constructorClassTreeFromList(domainList)); const treeData = addPathInTreeData(constructorClassTreeFromList(domainList));
@@ -77,13 +77,7 @@ const DomainListTree: FC<DomainListProps> = ({
const targetNodeData = classList.filter((item: any) => { const targetNodeData = classList.filter((item: any) => {
return item.id === selectedKeys; return item.id === selectedKeys;
})[0]; })[0];
onTreeSelected?.(); onTreeSelected?.(targetNodeData);
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: selectedKeys,
selectDomainName: projectName,
domainData: targetNodeData,
});
}; };
const editProject = async (values: any) => { const editProject = async (values: any) => {
@@ -134,7 +128,7 @@ const DomainListTree: FC<DomainListProps> = ({
</span> </span>
{createDomainBtnVisible && ( {createDomainBtnVisible && (
<span className={styles.operation}> <span className={styles.operation}>
{Array.isArray(path) && path.length < 3 && ( {Array.isArray(path) && path.length < 2 && (
<PlusOutlined <PlusOutlined
className={styles.icon} className={styles.icon}
onClick={() => { onClick={() => {

View File

@@ -0,0 +1,135 @@
import { Tabs, Button } from 'antd';
import React from 'react';
import { connect } from 'umi';
import ClassDataSourceTable from './ClassDataSourceTable';
import ClassDimensionTable from './ClassDimensionTable';
import ClassMetricTable from './ClassMetricTable';
import PermissionSection from './Permission/PermissionSection';
import DatabaseSection from './Database/DatabaseSection';
import EntitySettingSection from './Entity/EntitySettingSection';
import OverView from './OverView';
import styles from './style.less';
import type { StateType } from '../model';
import { LeftOutlined } from '@ant-design/icons';
import { ISemantic } from '../data';
import SemanticGraphCanvas from '../SemanticGraphCanvas';
import type { Dispatch } from 'umi';
type Props = {
isModel: boolean;
activeKey: string;
modelList: ISemantic.IModelItem[];
handleModelChange: (model?: ISemantic.IModelItem) => void;
onBackDomainBtnClick?: () => void;
onMenuChange?: (menuKey: string) => void;
domainManger: StateType;
dispatch: Dispatch;
};
const DomainManagerTab: React.FC<Props> = ({
isModel,
activeKey,
modelList,
handleModelChange,
onBackDomainBtnClick,
onMenuChange,
}) => {
const defaultTabKey = 'xflow';
const tabItem = [
{
label: '模型',
key: 'overview',
children: (
<OverView
modelList={modelList}
onModelChange={(model) => {
handleModelChange(model);
}}
/>
),
},
{
label: '数据库',
key: 'dataBase',
children: <DatabaseSection />,
},
{
label: '权限管理',
key: 'permissonSetting',
children: <PermissionSection permissionTarget={'domain'} />,
},
];
const isModelItem = [
{
label: '画布',
key: 'xflow',
children: (
<div style={{ width: '100%', marginTop: -20 }}>
<SemanticGraphCanvas />
</div>
),
},
{
label: '数据源',
key: 'dataSource',
children: <ClassDataSourceTable />,
},
{
label: '维度',
key: 'dimenstion',
children: <ClassDimensionTable />,
},
{
label: '指标',
key: 'metric',
children: <ClassMetricTable />,
},
{
label: '实体',
key: 'entity',
children: <EntitySettingSection />,
},
{
label: '权限管理',
key: 'permissonSetting',
children: <PermissionSection permissionTarget={'model'} />,
},
];
return (
<>
<Tabs
className={styles.tab}
items={!isModel ? tabItem : isModelItem}
activeKey={activeKey || defaultTabKey}
destroyInactiveTabPane
tabBarExtraContent={
isModel ? (
<Button
type="primary"
icon={<LeftOutlined />}
onClick={() => {
onBackDomainBtnClick?.();
}}
style={{ marginRight: 10 }}
>
</Button>
) : undefined
}
onChange={(menuKey: string) => {
onMenuChange?.(menuKey);
}}
/>
</>
);
};
export default connect(({ domainManger }: { domainManger: StateType }) => ({
domainManger,
}))(DomainManagerTab);

View File

@@ -143,7 +143,7 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
}; };
const { code, msg, data } = await saveDomainExtendQuery({ const { code, msg, data } = await saveDomainExtendQuery({
[chatConfigKey]: params, [chatConfigKey]: params,
domainId, // domainId,
id, id,
}); });
if (code === 200) { if (code === 200) {

View File

@@ -126,7 +126,7 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
const { code, msg } = await saveDomainExtendQuery({ const { code, msg } = await saveDomainExtendQuery({
[chatConfigKey]: params, [chatConfigKey]: params,
domainId, // domainId,
id, id,
}); });
if (code === 200) { if (code === 200) {

View File

@@ -1,22 +1,22 @@
import { useEffect, useState, forwardRef, useImperativeHandle } from 'react'; import { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
import type { ForwardRefRenderFunction } from 'react'; import type { ForwardRefRenderFunction } from 'react';
import { message, Form, Input, Select, Button } from 'antd'; import { message, Form, Input, Select, Button } from 'antd';
import { updateDomain } from '../../service'; import { updateModel } from '../../service';
import type { ISemantic } from '../../data'; import type { ISemantic } from '../../data';
import { formLayout } from '@/components/FormHelper/utils'; import { formLayout } from '@/components/FormHelper/utils';
import styles from '../style.less'; import styles from '../style.less';
type Props = { type Props = {
domainData?: ISemantic.IDomainItem; modelData?: ISemantic.IModelItem;
dimensionList: ISemantic.IDimensionList; dimensionList: ISemantic.IDimensionList;
domainId: number; modelId: number;
onSubmit: () => void; onSubmit: () => void;
}; };
const FormItem = Form.Item; const FormItem = Form.Item;
const EntityCreateForm: ForwardRefRenderFunction<any, Props> = ( const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
{ domainData, dimensionList, domainId, onSubmit }, { modelData, dimensionList, modelId, onSubmit },
ref, ref,
) => { ) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
@@ -27,15 +27,15 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
useEffect(() => { useEffect(() => {
form.resetFields(); form.resetFields();
if (!domainData?.entity) { if (!modelData?.entity) {
return; return;
} }
const { entity } = domainData; const { entity } = modelData;
form.setFieldsValue({ form.setFieldsValue({
...entity, ...entity,
name: entity.names.join(','), name: entity.names.join(','),
}); });
}, [domainData]); }, [modelData]);
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
getFormValidateFields, getFormValidateFields,
@@ -54,14 +54,14 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
const saveEntity = async () => { const saveEntity = async () => {
const values = await form.validateFields(); const values = await form.validateFields();
const { name } = values; const { name } = values;
const { code, msg, data } = await updateDomain({ const { code, msg, data } = await updateModel({
...domainData, ...modelData,
entity: { entity: {
...values, ...values,
names: name.split(','), names: name.split(','),
}, },
id: domainId, id: modelId,
domainId, modelId,
}); });
if (code === 200) { if (code === 200) {
@@ -79,20 +79,11 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
<FormItem hidden={true} name="id" label="ID"> <FormItem hidden={true} name="id" label="ID">
<Input placeholder="id" /> <Input placeholder="id" />
</FormItem> </FormItem>
<FormItem <FormItem name="name" label="实体别名">
name="name"
label="实体别名"
// rules={[{ required: true, message: '请输入实体别名' }]}
>
<Input placeholder="请输入实体别名,多个名称以英文逗号分隔" /> <Input placeholder="请输入实体别名,多个名称以英文逗号分隔" />
</FormItem> </FormItem>
<FormItem <FormItem name="entityId" label="唯一标识">
name="entityId"
label="唯一标识"
// rules={[{ required: true, message: '请选择实体标识' }]}
>
<Select <Select
// mode="multiple"
allowClear allowClear
style={{ width: '100%' }} style={{ width: '100%' }}
// filterOption={(inputValue: string, item: any) => { // filterOption={(inputValue: string, item: any) => {

View File

@@ -22,22 +22,22 @@ const EntitySection: React.FC<Props> = ({
dispatch, dispatch,
chatConfigType = ChatConfigType.DETAIL, chatConfigType = ChatConfigType.DETAIL,
}) => { }) => {
const { selectDomainId, dimensionList, metricList } = domainManger; const { selectDomainId, selectModelId: modelId, dimensionList, metricList } = domainManger;
const [entityData, setentityData] = useState<IChatConfig.IChatRichConfig>(); const [entityData, setEntityData] = useState<IChatConfig.IChatRichConfig>();
const queryThemeListData: any = async () => { const queryThemeListData: any = async () => {
const { code, data } = await getDomainExtendDetailConfig({ const { code, data } = await getDomainExtendDetailConfig({
domainId: selectDomainId, modelId,
}); });
if (code === 200) { if (code === 200) {
const { chatAggRichConfig, chatDetailRichConfig, id, domainId } = data; const { chatAggRichConfig, chatDetailRichConfig, id, domainId, modelId } = data;
if (chatConfigType === ChatConfigType.DETAIL) { if (chatConfigType === ChatConfigType.DETAIL) {
setentityData({ ...chatDetailRichConfig, id, domainId }); setEntityData({ ...chatDetailRichConfig, id, domainId, modelId });
} }
if (chatConfigType === ChatConfigType.AGG) { if (chatConfigType === ChatConfigType.AGG) {
setentityData({ ...chatAggRichConfig, id, domainId }); setEntityData({ ...chatAggRichConfig, id, domainId, modelId });
} }
return; return;
} }
@@ -50,8 +50,11 @@ const EntitySection: React.FC<Props> = ({
}; };
useEffect(() => { useEffect(() => {
if (!modelId) {
return;
}
initPage(); initPage();
}, [selectDomainId]); }, [modelId]);
return ( return (
<div style={{ width: 800, margin: '0 auto' }}> <div style={{ width: 800, margin: '0 auto' }}>

View File

@@ -3,7 +3,7 @@ import React, { useState, useEffect, useRef } from 'react';
import type { Dispatch } from 'umi'; import type { Dispatch } from 'umi';
import { connect } from 'umi'; import { connect } from 'umi';
import type { StateType } from '../../model'; import type { StateType } from '../../model';
import { getDomainDetail } from '../../service'; import { getModelDetail } from '../../service';
import ProCard from '@ant-design/pro-card'; import ProCard from '@ant-design/pro-card';
import EntityCreateForm from './EntityCreateForm'; import EntityCreateForm from './EntityCreateForm';
import type { ISemantic } from '../../data'; import type { ISemantic } from '../../data';
@@ -14,19 +14,19 @@ type Props = {
}; };
const EntitySettingSection: React.FC<Props> = ({ domainManger }) => { const EntitySettingSection: React.FC<Props> = ({ domainManger }) => {
const { selectDomainId, dimensionList } = domainManger; const { dimensionList, selectModelId: modelId } = domainManger;
const [domainData, setDomainData] = useState<ISemantic.IDomainItem>(); const [modelData, setModelData] = useState<ISemantic.IModelItem>();
const entityCreateRef = useRef<any>({}); const entityCreateRef = useRef<any>({});
const queryDomainData: any = async () => { const queryDomainData: any = async () => {
const { code, data } = await getDomainDetail({ const { code, data } = await getModelDetail({
domainId: selectDomainId, modelId,
}); });
if (code === 200) { if (code === 200) {
setDomainData(data); setModelData(data);
return; return;
} }
@@ -40,7 +40,7 @@ const EntitySettingSection: React.FC<Props> = ({ domainManger }) => {
useEffect(() => { useEffect(() => {
initPage(); initPage();
}, [selectDomainId]); }, [modelId]);
return ( return (
<div style={{ width: 800, margin: '0 auto' }}> <div style={{ width: 800, margin: '0 auto' }}>
@@ -49,8 +49,8 @@ const EntitySettingSection: React.FC<Props> = ({ domainManger }) => {
<ProCard title="实体" bordered> <ProCard title="实体" bordered>
<EntityCreateForm <EntityCreateForm
ref={entityCreateRef} ref={entityCreateRef}
domainId={Number(selectDomainId)} modelId={Number(modelId)}
domainData={domainData} modelData={modelData}
dimensionList={dimensionList} dimensionList={dimensionList}
onSubmit={() => { onSubmit={() => {
queryDomainData(); queryDomainData();

View File

@@ -12,14 +12,14 @@ type Props = {
}; };
const RecommendedQuestionsSection: React.FC<Props> = ({ domainManger }) => { const RecommendedQuestionsSection: React.FC<Props> = ({ domainManger }) => {
const { selectDomainId } = domainManger; const { selectModelId: modelId } = domainManger;
const [questionData, setQuestionData] = useState<string[]>([]); const [questionData, setQuestionData] = useState<string[]>([]);
const [currentRecordId, setCurrentRecordId] = useState<number>(0); const [currentRecordId, setCurrentRecordId] = useState<number>(0);
const queryThemeListData: any = async () => { const queryThemeListData: any = async () => {
const { code, data } = await getDomainExtendConfig({ const { code, data } = await getDomainExtendConfig({
domainId: selectDomainId, modelId,
}); });
if (code === 200) { if (code === 200) {
@@ -51,7 +51,7 @@ const RecommendedQuestionsSection: React.FC<Props> = ({ domainManger }) => {
return { question }; return { question };
}), }),
id: currentRecordId, id: currentRecordId,
domainId: selectDomainId, modelId,
}); });
if (code === 200) { if (code === 200) {
@@ -65,8 +65,11 @@ const RecommendedQuestionsSection: React.FC<Props> = ({ domainManger }) => {
}; };
useEffect(() => { useEffect(() => {
if (!modelId) {
return;
}
initPage(); initPage();
}, [selectDomainId]); }, [modelId]);
return ( return (
<div style={{ width: 800, margin: '0 auto' }}> <div style={{ width: 800, margin: '0 auto' }}>

View File

@@ -16,15 +16,15 @@ import { SENSITIVE_LEVEL_OPTIONS } from '../constant';
import { formLayout } from '@/components/FormHelper/utils'; import { formLayout } from '@/components/FormHelper/utils';
import FormItemTitle from '@/components/FormHelper/FormItemTitle'; import FormItemTitle from '@/components/FormHelper/FormItemTitle';
import styles from './style.less'; import styles from './style.less';
import { getMeasureListByDomainId } from '../service'; import { getMeasureListByModelId } from '../service';
import { creatExprMetric, updateExprMetric } from '../service'; import { creatExprMetric, updateExprMetric } from '../service';
import { ISemantic } from '../data'; import { ISemantic } from '../data';
import { history } from 'umi'; import { history } from 'umi';
import { check } from 'prettier';
export type CreateFormProps = { export type CreateFormProps = {
datasourceId?: number; datasourceId?: number;
domainId: number; domainId: number;
modelId: number;
createModalVisible: boolean; createModalVisible: boolean;
metricItem: any; metricItem: any;
onCancel?: () => void; onCancel?: () => void;
@@ -39,6 +39,7 @@ const { Option } = Select;
const MetricInfoCreateForm: React.FC<CreateFormProps> = ({ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
datasourceId, datasourceId,
domainId, domainId,
modelId,
onCancel, onCancel,
createModalVisible, createModalVisible,
metricItem, metricItem,
@@ -65,7 +66,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
const backward = () => setCurrentStep(currentStep - 1); const backward = () => setCurrentStep(currentStep - 1);
const queryClassMeasureList = async () => { const queryClassMeasureList = async () => {
const { code, data } = await getMeasureListByDomainId(domainId); const { code, data } = await getMeasureListByModelId(modelId);
if (code === 200) { if (code === 200) {
setClassMeasureList(data); setClassMeasureList(data);
if (datasourceId) { if (datasourceId) {
@@ -147,7 +148,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
const saveMetric = async (fieldsValue: any) => { const saveMetric = async (fieldsValue: any) => {
const queryParams = { const queryParams = {
domainId, modelId,
...fieldsValue, ...fieldsValue,
}; };
const { typeParams } = queryParams; const { typeParams } = queryParams;
@@ -351,7 +352,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
type="primary" type="primary"
key="console" key="console"
onClick={() => { onClick={() => {
history.replace(`/semanticModel/${domainId}/dataSource`); history.replace(`/semanticModel/${domainId}/${modelId}/dataSource`);
onCancel?.(); onCancel?.();
}} }}
> >

View File

@@ -0,0 +1,95 @@
import React, { useState, useEffect } from 'react';
import { Form, Button, Modal, Input, Switch } from 'antd';
import styles from './style.less';
import { message } from 'antd';
import { formLayout } from '@/components/FormHelper/utils';
import { createModel, updateModel } from '../service';
const FormItem = Form.Item;
export type ModelCreateFormModalProps = {
domainId: number;
basicInfo: any;
onCancel: () => void;
onSubmit: (values: any) => void;
};
const ModelCreateFormModal: React.FC<ModelCreateFormModalProps> = (props) => {
const { basicInfo, domainId, onCancel, onSubmit } = props;
const [formVals, setFormVals] = useState<any>(basicInfo);
const [saveLoading, setSaveLoading] = useState(false);
const [form] = Form.useForm();
useEffect(() => {
form.setFieldsValue(basicInfo);
}, [basicInfo]);
const handleConfirm = async () => {
const fieldsValue = await form.validateFields();
const columnsValue = { ...fieldsValue, isUnique: 1, domainId };
const submitData = { ...formVals, ...columnsValue };
setFormVals(submitData);
setSaveLoading(true);
const { code, msg } = await (!submitData.id ? createModel : updateModel)(submitData);
setSaveLoading(false);
if (code === 200) {
onSubmit?.(submitData);
} else {
message.error(msg);
}
};
const footer = (
<>
<Button onClick={onCancel}></Button>
<Button type="primary" loading={saveLoading} onClick={handleConfirm}>
</Button>
</>
);
return (
<Modal
width={640}
bodyStyle={{ padding: '32px 40px 48px' }}
destroyOnClose
title={'模型信息'}
open={true}
footer={footer}
onCancel={onCancel}
>
<Form
{...formLayout}
form={form}
initialValues={{
...formVals,
}}
className={styles.form}
>
<FormItem
name="name"
label="模型名称"
rules={[{ required: true, message: '请输入模型名称!' }]}
>
<Input placeholder="模型名称不可重复" />
</FormItem>
<FormItem
name="bizName"
label="模型英文名称"
rules={[{ required: true, message: '请输入模型英文名称!' }]}
>
<Input placeholder="请输入模型英文名称" />
</FormItem>
<FormItem name="description" label="模型描述">
<Input.TextArea placeholder="模型描述" />
</FormItem>
<FormItem name="isUnique" label="是否唯一" hidden={true}>
<Switch size="small" checked={true} />
</FormItem>
</Form>
</Modal>
);
};
export default ModelCreateFormModal;

View File

@@ -1,23 +1,36 @@
import { CheckCard } from '@ant-design/pro-components'; import { CheckCard } from '@ant-design/pro-components';
import React from 'react'; import React, { useState } from 'react';
import { Button, Dropdown, message, Popconfirm } from 'antd';
import { PlusOutlined, EllipsisOutlined } from '@ant-design/icons';
import { ISemantic } from '../data'; import { ISemantic } from '../data';
import { connect } from 'umi'; import { connect } from 'umi';
import icon from '../../../assets/icon/cloudEditor.svg'; import icon from '../../../assets/icon/cloudEditor.svg';
import type { Dispatch } from 'umi'; import type { Dispatch } from 'umi';
import type { StateType } from '../model'; import type { StateType } from '../model';
import { formatNumber } from '../../../utils/utils'; import { formatNumber } from '../../../utils/utils';
import { deleteModel } from '../service';
import ModelCreateFormModal from './ModelCreateFormModal';
import styles from './style.less'; import styles from './style.less';
type Props = { type Props = {
modelList: ISemantic.IDomainItem[]; disabledEdit?: boolean;
modelList: ISemantic.IModelItem[];
onModelChange?: (model?: ISemantic.IModelItem) => void;
domainManger: StateType; domainManger: StateType;
dispatch: Dispatch; dispatch: Dispatch;
}; };
const OverView: React.FC<Props> = ({ domainManger, dispatch, modelList }) => { const OverView: React.FC<Props> = ({
const { selectDomainId } = domainManger; modelList,
disabledEdit = false,
onModelChange,
domainManger,
}) => {
const { selectDomainId, selectModelId } = domainManger;
const [currentModel, setCurrentModel] = useState<any>({});
const [modelCreateFormModalVisible, setModelCreateFormModalVisible] = useState<boolean>(false);
const extraNode = (model: ISemantic.IDomainItem) => { const descNode = (model: ISemantic.IDomainItem) => {
const { metricCnt, dimensionCnt } = model; const { metricCnt, dimensionCnt } = model;
return ( return (
<div className={styles.overviewExtraContainer}> <div className={styles.overviewExtraContainer}>
@@ -40,33 +53,103 @@ const OverView: React.FC<Props> = ({ domainManger, dispatch, modelList }) => {
</div> </div>
); );
}; };
const extraNode = (model: ISemantic.IDomainItem) => {
return (
<Dropdown
placement="top"
menu={{
onClick: ({ key, domEvent }) => {
domEvent.stopPropagation();
if (key === 'edit') {
setCurrentModel(model);
setModelCreateFormModalVisible(true);
}
},
items: [
{
label: '编辑',
key: 'edit',
},
{
label: (
<Popconfirm
title="确认删除?"
okText="是"
cancelText="否"
onConfirm={async () => {
const { code, msg } = await deleteModel(model.id);
if (code === 200) {
onModelChange?.();
} else {
message.error(msg);
}
}}
>
<a key="modelDeleteBtn"></a>
</Popconfirm>
),
key: 'delete',
},
],
}}
>
<EllipsisOutlined
style={{ fontSize: 22, color: 'rgba(0,0,0,0.5)' }}
onClick={(e) => e.stopPropagation()}
/>
</Dropdown>
);
};
return ( return (
<> <div style={{ padding: '0px 20px 20px' }}>
<CheckCard.Group value={selectDomainId} defaultValue={selectDomainId}> {!disabledEdit && (
<div style={{ paddingBottom: '20px' }}>
<Button
onClick={() => {
setModelCreateFormModalVisible(true);
}}
type="primary"
>
<PlusOutlined />
</Button>
</div>
)}
<CheckCard.Group value={selectModelId} defaultValue={selectModelId}>
{modelList && {modelList &&
modelList.map((model: ISemantic.IDomainItem) => { modelList.map((model: ISemantic.IDomainItem) => {
return ( return (
<CheckCard <CheckCard
avatar={icon} avatar={icon}
title={model.name} title={`${model.name}`}
key={model.id} key={model.id}
value={model.id} value={model.id}
// description={model.description || '模型描述...'} description={descNode(model)}
description={extraNode(model)} extra={!disabledEdit && extraNode(model)}
onClick={() => { onClick={() => {
const { id, name } = model; onModelChange?.(model);
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: id,
selectDomainName: name,
domainData: model,
});
}} }}
/> />
); );
})} })}
</CheckCard.Group> </CheckCard.Group>
</> {modelCreateFormModalVisible && (
<ModelCreateFormModal
domainId={selectDomainId}
basicInfo={currentModel}
onSubmit={() => {
setModelCreateFormModalVisible(false);
onModelChange?.();
}}
onCancel={() => {
setModelCreateFormModalVisible(false);
}}
/>
)}
</div>
); );
}; };

View File

@@ -6,10 +6,11 @@ import { connect } from 'umi';
import type { Dispatch } from 'umi'; import type { Dispatch } from 'umi';
import type { StateType } from '../../model'; import type { StateType } from '../../model';
import FormItemTitle from '@/components/FormHelper/FormItemTitle'; import FormItemTitle from '@/components/FormHelper/FormItemTitle';
import { updateDomain, getDomainDetail } from '../../service'; import { updateDomain, updateModel, getDomainDetail, getModelDetail } from '../../service';
import styles from '../style.less'; import styles from '../style.less';
type Props = { type Props = {
permissionTarget: 'model' | 'domain';
dispatch: Dispatch; dispatch: Dispatch;
domainManger: StateType; domainManger: StateType;
onSubmit?: (data?: any) => void; onSubmit?: (data?: any) => void;
@@ -18,15 +19,22 @@ type Props = {
const FormItem = Form.Item; const FormItem = Form.Item;
const PermissionAdminForm: React.FC<Props> = ({ domainManger, onValuesChange }) => { const PermissionAdminForm: React.FC<Props> = ({
permissionTarget,
domainManger,
onValuesChange,
}) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [isOpenState, setIsOpenState] = useState<boolean>(true); const [isOpenState, setIsOpenState] = useState<boolean>(true);
const [classDetail, setClassDetail] = useState<any>({}); const [classDetail, setClassDetail] = useState<any>({});
const { selectDomainId } = domainManger; const { selectModelId: modelId, selectDomainId } = domainManger;
const { APP_TARGET } = process.env; const { APP_TARGET } = process.env;
const queryClassDetail = async (domainId: number) => { const queryClassDetail = async () => {
const { code, msg, data } = await getDomainDetail({ domainId }); const selectId = permissionTarget === 'model' ? modelId : selectDomainId;
const { code, msg, data } = await (permissionTarget === 'model'
? getModelDetail
: getDomainDetail)({ modelId: selectId });
if (code === 200) { if (code === 200) {
setClassDetail(data); setClassDetail(data);
const fieldsValue = { const fieldsValue = {
@@ -44,8 +52,8 @@ const PermissionAdminForm: React.FC<Props> = ({ domainManger, onValuesChange })
}; };
useEffect(() => { useEffect(() => {
queryClassDetail(selectDomainId); queryClassDetail();
}, [selectDomainId]); }, [modelId]);
const saveAuth = async () => { const saveAuth = async () => {
const values = await form.validateFields(); const values = await form.validateFields();
@@ -57,9 +65,10 @@ const PermissionAdminForm: React.FC<Props> = ({ domainManger, onValuesChange })
viewers, viewers,
isOpen: isOpen ? 1 : 0, isOpen: isOpen ? 1 : 0,
}; };
const { code, msg } = await updateDomain(queryClassData); const { code, msg } = await (permissionTarget === 'model' ? updateModel : updateDomain)(
queryClassData,
);
if (code === 200) { if (code === 200) {
// message.success('保存成功');
return; return;
} }
message.error(msg); message.error(msg);
@@ -123,16 +132,6 @@ const PermissionAdminForm: React.FC<Props> = ({ domainManger, onValuesChange })
</FormItem> </FormItem>
</> </>
)} )}
{/* <FormItem>
<Button
type="primary"
onClick={() => {
saveAuth();
}}
>
保 存
</Button>
</FormItem> */}
</Form> </Form>
</> </>
); );

View File

@@ -14,7 +14,6 @@ import styles from '../style.less';
type Props = { type Props = {
domainManger: StateType; domainManger: StateType;
permissonData: any; permissonData: any;
domainId: number;
onCancel: () => void; onCancel: () => void;
visible: boolean; visible: boolean;
onSubmit: (params?: any) => void; onSubmit: (params?: any) => void;
@@ -25,11 +24,10 @@ const PermissionCreateDrawer: React.FC<Props> = ({
domainManger, domainManger,
visible, visible,
permissonData, permissonData,
domainId,
onCancel, onCancel,
onSubmit, onSubmit,
}) => { }) => {
const { dimensionList, metricList } = domainManger; const { dimensionList, metricList, selectModelId: modelId } = domainManger;
const [form] = Form.useForm(); const [form] = Form.useForm();
const basicInfoFormRef = useRef<any>(null); const basicInfoFormRef = useRef<any>(null);
const [selectedDimensionKeyList, setSelectedDimensionKeyList] = useState<string[]>([]); const [selectedDimensionKeyList, setSelectedDimensionKeyList] = useState<string[]>([]);
@@ -65,7 +63,7 @@ const PermissionCreateDrawer: React.FC<Props> = ({
metrics: selectedMetricKeyList, metrics: selectedMetricKeyList,
}, },
], ],
domainId, modelId,
}); });
if (code === 200) { if (code === 200) {
@@ -136,11 +134,7 @@ const PermissionCreateDrawer: React.FC<Props> = ({
<div style={{ overflow: 'auto', margin: '0 auto', width: '1200px' }}> <div style={{ overflow: 'auto', margin: '0 auto', width: '1200px' }}>
<Space direction="vertical" style={{ width: '100%' }} size={20}> <Space direction="vertical" style={{ width: '100%' }} size={20}>
<ProCard title="基本信息" bordered> <ProCard title="基本信息" bordered>
<PermissionCreateForm <PermissionCreateForm ref={basicInfoFormRef} permissonData={permissonData} />
ref={basicInfoFormRef}
permissonData={permissonData}
domainId={domainId}
/>
</ProCard> </ProCard>
<ProCard title="列权限" bordered tooltip="仅对敏感度为高的指标/维度进行授权"> <ProCard title="列权限" bordered tooltip="仅对敏感度为高的指标/维度进行授权">

View File

@@ -6,7 +6,6 @@ import SelectTMEPerson from '@/components/SelectTMEPerson';
import { formLayout } from '@/components/FormHelper/utils'; import { formLayout } from '@/components/FormHelper/utils';
import styles from '../style.less'; import styles from '../style.less';
type Props = { type Props = {
domainId: number;
permissonData: any; permissonData: any;
onSubmit?: (data?: any) => void; onSubmit?: (data?: any) => void;
onValuesChange?: (value, values) => void; onValuesChange?: (value, values) => void;

View File

@@ -8,20 +8,20 @@ import PermissionTable from './PermissionTable';
import PermissionAdminForm from './PermissionAdminForm'; import PermissionAdminForm from './PermissionAdminForm';
type Props = { type Props = {
permissionTarget: 'model' | 'domain';
dispatch: Dispatch; dispatch: Dispatch;
domainManger: StateType; domainManger: StateType;
}; };
const PermissionSection: React.FC<Props> = () => { const PermissionSection: React.FC<Props> = ({ permissionTarget }) => {
return ( return (
<> <>
<div> <div>
<Space direction="vertical" style={{ width: '100%' }} size={20}> <Space direction="vertical" style={{ width: '100%' }} size={20}>
<ProCard title="邀请成员" bordered> <ProCard title="邀请成员" bordered>
<PermissionAdminForm /> <PermissionAdminForm permissionTarget={permissionTarget} />
</ProCard> </ProCard>
{permissionTarget === 'model' && <PermissionTable />}
<PermissionTable />
</Space> </Space>
</div> </div>
</> </>

View File

@@ -19,7 +19,7 @@ type Props = {
const PermissionTable: React.FC<Props> = ({ domainManger }) => { const PermissionTable: React.FC<Props> = ({ domainManger }) => {
const { APP_TARGET } = process.env; const { APP_TARGET } = process.env;
const isInner = APP_TARGET === 'inner'; const isInner = APP_TARGET === 'inner';
const { dimensionList, metricList, selectDomainId } = domainManger; const { dimensionList, metricList, selectModelId: modelId } = domainManger;
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false); const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const [permissonData, setPermissonData] = useState<any>({}); const [permissonData, setPermissonData] = useState<any>({});
@@ -37,7 +37,7 @@ const PermissionTable: React.FC<Props> = ({ domainManger }) => {
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
const queryListData = async () => { const queryListData = async () => {
const { code, data } = await getGroupAuthInfo(selectDomainId); const { code, data } = await getGroupAuthInfo(modelId);
if (code === 200) { if (code === 200) {
setIntentionList(data); setIntentionList(data);
return; return;
@@ -46,10 +46,10 @@ const PermissionTable: React.FC<Props> = ({ domainManger }) => {
}; };
useEffect(() => { useEffect(() => {
if (selectDomainId) { if (modelId) {
queryListData(); queryListData();
} }
}, [selectDomainId]); }, [modelId]);
const queryDepartmentData = async () => { const queryDepartmentData = async () => {
const { code, data } = await getOrganizationTree(); const { code, data } = await getOrganizationTree();
@@ -184,7 +184,7 @@ const PermissionTable: React.FC<Props> = ({ domainManger }) => {
return ( return (
<Space> <Space>
<a <a
key="classEditBtn" key="permissionEditBtn"
onClick={() => { onClick={() => {
setPermissonData(record); setPermissonData(record);
setCreateModalVisible(true); setCreateModalVisible(true);
@@ -216,7 +216,7 @@ const PermissionTable: React.FC<Props> = ({ domainManger }) => {
cancelText="否" cancelText="否"
onConfirm={async () => { onConfirm={async () => {
const { code, msg } = await removeGroupAuth({ const { code, msg } = await removeGroupAuth({
domainId: record.domainId, modelId: record.modelId,
groupId: record.groupId, groupId: record.groupId,
}); });
if (code === 200) { if (code === 200) {
@@ -228,7 +228,7 @@ const PermissionTable: React.FC<Props> = ({ domainManger }) => {
}} }}
> >
<a <a
key="classEditBtn" key="permissionDeleteBtn"
onClick={() => { onClick={() => {
setPermissonData(record); setPermissonData(record);
}} }}
@@ -277,7 +277,6 @@ const PermissionTable: React.FC<Props> = ({ domainManger }) => {
/> />
{createModalVisible && ( {createModalVisible && (
<PermissionCreateDrawer <PermissionCreateDrawer
domainId={Number(selectDomainId)}
visible={createModalVisible} visible={createModalVisible}
permissonData={permissonData} permissonData={permissonData}
onSubmit={() => { onSubmit={() => {

View File

@@ -25,7 +25,6 @@ const ProjectInfoForm: React.FC<ProjectInfoFormProps> = (props) => {
const handleConfirm = async () => { const handleConfirm = async () => {
const fieldsValue = await form.validateFields(); const fieldsValue = await form.validateFields();
// const columnsValue = { ...fieldsValue, isUnique: fieldsValue.isUnique === true ? 1 : 0 };
const columnsValue = { ...fieldsValue, isUnique: 1 }; const columnsValue = { ...fieldsValue, isUnique: 1 };
setFormVals({ ...formVals, ...columnsValue }); setFormVals({ ...formVals, ...columnsValue });
setSaveLoading(true); setSaveLoading(true);
@@ -97,24 +96,11 @@ const ProjectInfoForm: React.FC<ProjectInfoFormProps> = (props) => {
> >
<Input placeholder="请输入主题域英文名称" /> <Input placeholder="请输入主题域英文名称" />
</FormItem> </FormItem>
<FormItem name="description" label="主题域描述"> <FormItem name="description" label="主题域描述" hidden={true}>
<Input.TextArea placeholder="主题域描述" /> <Input.TextArea placeholder="主题域描述" />
</FormItem> </FormItem>
<FormItem name="isUnique" label="是否唯一" hidden={true}> <FormItem name="isUnique" label="是否唯一" hidden={true}>
<Switch <Switch size="small" checked={true} />
size="small"
checked={true}
// onChange={(checked) => {
// setFormVals({ ...formVals, isUnique: checked });
// }}
/>
{/* <Switch
size="small"
checked={formVals.isUnique ? true : false}
onChange={(checked) => {
setFormVals({ ...formVals, isUnique: checked });
}}
/> */}
</FormItem> </FormItem>
</Form> </Form>
</Modal> </Modal>

View File

@@ -94,6 +94,30 @@ export declare namespace ISemantic {
metricCnt?: number; metricCnt?: number;
} }
interface IModelItem {
createdBy?: string;
updatedBy?: string;
createdAt?: string;
updatedAt?: string;
id: number;
name: string;
bizName: string;
description: any;
status?: number;
typeEnum?: any;
sensitiveLevel?: number;
parentId: number;
fullPath?: string;
viewers?: any[];
viewOrgs?: any[];
admins?: string[];
adminOrgs?: any[];
isOpen?: number;
entity?: { entityId: number; names: string[] };
dimensionCnt?: number;
metricCnt?: number;
}
interface IDimensionItem { interface IDimensionItem {
createdBy: string; createdBy: string;
updatedBy: string; updatedBy: string;

View File

@@ -6,14 +6,17 @@ import { getDimensionList, queryMetric, excuteSql, getDatabaseByDomainId } from
export type StateType = { export type StateType = {
current: number; current: number;
pageSize: number; pageSize: number;
selectModelId: number;
selectDomainId: number; selectDomainId: number;
selectDomainName: string; selectDomainName: string;
selectModelName: string;
dimensionList: ISemantic.IDimensionList; dimensionList: ISemantic.IDimensionList;
metricList: ISemantic.IMetricList; metricList: ISemantic.IMetricList;
searchParams: Record<string, any>; searchParams: Record<string, any>;
dataBaseResultColsMap: any; dataBaseResultColsMap: any;
dataBaseConfig: any; dataBaseConfig: any;
domainData?: ISemantic.IDomainItem; domainData?: ISemantic.IDomainItem;
modelData?: ISemantic.IDomainItem;
domainList: ISemantic.IDomainItem[]; domainList: ISemantic.IDomainItem[];
}; };
@@ -28,6 +31,7 @@ export type ModelType = {
}; };
reducers: { reducers: {
setSelectDomain: Reducer<StateType>; setSelectDomain: Reducer<StateType>;
setSelectModel: Reducer<StateType>;
setDomainList: Reducer<StateType>; setDomainList: Reducer<StateType>;
setPagination: Reducer<StateType>; setPagination: Reducer<StateType>;
setDimensionList: Reducer<StateType>; setDimensionList: Reducer<StateType>;
@@ -42,7 +46,10 @@ export const defaultState: StateType = {
current: 1, current: 1,
pageSize: 20, pageSize: 20,
selectDomainId: 0, selectDomainId: 0,
selectModelId: 0,
modelData: undefined,
selectDomainName: '', selectDomainName: '',
selectModelName: '',
searchParams: {}, searchParams: {},
dimensionList: [], dimensionList: [],
metricList: [], metricList: [],
@@ -123,6 +130,14 @@ const Model: ModelType = {
domainData: action.domainData, domainData: action.domainData,
}; };
}, },
setSelectModel(state = defaultState, action) {
return {
...state,
selectModelId: action.selectModelId,
selectModelName: action.selectModelName,
modelData: action.modelData,
};
},
setDomainList(state = defaultState, action) { setDomainList(state = defaultState, action) {
return { return {
...state, ...state,

View File

@@ -12,11 +12,11 @@ export function getDomainList(): Promise<any> {
} }
export function getDatasourceList(data: any): Promise<any> { export function getDatasourceList(data: any): Promise<any> {
return request.get(`${process.env.API_BASE_URL}datasource/getDatasourceList/${data.domainId}`); return request.get(`${process.env.API_BASE_URL}datasource/getDatasourceList/${data.modelId}`);
} }
export function getDomainDetail(data: any): Promise<any> { export function getDomainDetail(data: any): Promise<any> {
return request.get(`${process.env.API_BASE_URL}domain/getDomain/${data.domainId}`); return request.get(`${process.env.API_BASE_URL}domain/getDomain/${data.modelId}`);
} }
export function createDomain(data: any): Promise<any> { export function createDomain(data: any): Promise<any> {
@@ -44,9 +44,15 @@ export function updateDatasource(data: any): Promise<any> {
} }
export function getDimensionList(data: any): Promise<any> { export function getDimensionList(data: any): Promise<any> {
const { domainId } = data; const { domainId, modelId } = data;
const queryParams = { const queryParams = {
data: { current: 1, pageSize: 999999, ...data, ...(domainId ? { domainIds: [domainId] } : {}) }, data: {
current: 1,
pageSize: 999999,
...data,
...(domainId ? { domainIds: [domainId] } : {}),
...(modelId ? { modelIds: [modelId] } : {}),
},
}; };
if (getRunningEnv() === 'chat') { if (getRunningEnv() === 'chat') {
return request.post(`${process.env.CHAT_API_BASE_URL}conf/dimension/page`, queryParams); return request.post(`${process.env.CHAT_API_BASE_URL}conf/dimension/page`, queryParams);
@@ -67,9 +73,15 @@ export function updateDimension(data: any): Promise<any> {
} }
export function queryMetric(data: any): Promise<any> { export function queryMetric(data: any): Promise<any> {
const { domainId } = data; const { domainId, modelId } = data;
const queryParams = { const queryParams = {
data: { current: 1, pageSize: 999999, ...data, ...(domainId ? { domainIds: [domainId] } : {}) }, data: {
current: 1,
pageSize: 999999,
...data,
...(domainId ? { domainIds: [domainId] } : {}),
...(modelId ? { modelIds: [modelId] } : {}),
},
}; };
if (getRunningEnv() === 'chat') { if (getRunningEnv() === 'chat') {
return request.post(`${process.env.CHAT_API_BASE_URL}conf/metric/page`, queryParams); return request.post(`${process.env.CHAT_API_BASE_URL}conf/metric/page`, queryParams);
@@ -89,8 +101,8 @@ export function updateExprMetric(data: any): Promise<any> {
}); });
} }
export function getMeasureListByDomainId(domainId: number): Promise<any> { export function getMeasureListByModelId(modelId: number): Promise<any> {
return request.get(`${process.env.API_BASE_URL}datasource/getMeasureListOfDomain/${domainId}`); return request.get(`${process.env.API_BASE_URL}datasource/getMeasureListOfModel/${modelId}`);
} }
export function deleteDatasource(id: any): Promise<any> { export function deleteDatasource(id: any): Promise<any> {
@@ -117,12 +129,10 @@ export function deleteDomain(id: any): Promise<any> {
}); });
} }
export function getGroupAuthInfo(id: number): Promise<any> { export function getGroupAuthInfo(modelId: number): Promise<any> {
return request(`${process.env.AUTH_API_BASE_URL}queryGroup`, { return request(`${process.env.AUTH_API_BASE_URL}queryGroup`, {
method: 'GET', method: 'GET',
params: { params: { modelId },
domainId: id,
},
}); });
} }
@@ -169,7 +179,7 @@ export function getDomainExtendConfig(data: any): Promise<any> {
} }
export function getDomainExtendDetailConfig(data: any): Promise<any> { export function getDomainExtendDetailConfig(data: any): Promise<any> {
return request(`${process.env.CHAT_API_BASE_URL}conf/richDesc/${data.domainId}`, { return request(`${process.env.CHAT_API_BASE_URL}conf/richDesc/${data.modelId}`, {
method: 'GET', method: 'GET',
}); });
} }
@@ -246,7 +256,7 @@ export function testDatabaseConnect(data: SaveDatabaseParams): Promise<any> {
type ExcuteSqlParams = { type ExcuteSqlParams = {
sql: string; sql: string;
domainId: number; modelId: number;
}; };
// 执行脚本 // 执行脚本
@@ -272,3 +282,37 @@ export function getColumns(dbId: number, dbName: string, tableName: string): Pro
method: 'GET', method: 'GET',
}); });
} }
export function getModelList(domainId: number): Promise<any> {
if (getRunningEnv() === 'chat') {
return request(`${process.env.CHAT_API_BASE_URL}conf/modelList/${domainId}`, {
method: 'GET',
});
}
return request(`${process.env.API_BASE_URL}model/getModelList/${domainId}`, {
method: 'GET',
});
}
export function createModel(data: any): Promise<any> {
return request(`${process.env.API_BASE_URL}model/createModel`, {
method: 'POST',
data,
});
}
export function updateModel(data: any): Promise<any> {
return request(`${process.env.API_BASE_URL}model/updateModel`, {
method: 'POST',
data,
});
}
export function deleteModel(modelId: number): Promise<any> {
return request(`${process.env.API_BASE_URL}model/deleteModel/${modelId}`, {
method: 'DELETE',
});
}
export function getModelDetail(data: any): Promise<any> {
return request.get(`${process.env.API_BASE_URL}model/getModel/${data.modelId}`);
}

View File

@@ -175,12 +175,12 @@ declare namespace API {
comment: string; // 项目描述 comment: string; // 项目描述
creator: string; // 项目创建人 creator: string; // 项目创建人
projectType: number; // 项目类别 0-为私有项目 1-为公共项目 projectType: number; // 项目类别 0-为私有项目 1-为公共项目
childProjectList?: ProjectList; childDomainList?: DomainList;
children?: ProjectList; children?: DomainList;
value: string; value: string;
}; };
export type ProjectList = ProjectItem[]; export type DomainList = ProjectItem[];
// 数据实例详情 // 数据实例详情
export type DataInstanceDetail = { export type DataInstanceDetail = {