From c3d3b1146b4fc68931ab2cdfddc937f1637b194d Mon Sep 17 00:00:00 2001 From: tristanliu Date: Tue, 15 Aug 2023 10:40:58 +0800 Subject: [PATCH] [improvement][semantic-fe] Restructured the code to extract the question-answer settings and model management model controls into the OverviewContainer component. --- package-lock.json | 6 + .../packages/supersonic-fe/config/routes.ts | 12 +- .../supersonic-fe/src/enum/models/base.ts | 29 -- .../src/pages/SemanticModel/ChatSetting.tsx | 205 ------------- .../SemanticModel/ChatSetting/ChatSetting.tsx | 22 ++ .../ChatSetting/ChatSettingTab.tsx | 102 ++++++ .../components/DataSourceCreateForm.tsx | 7 +- .../Datasource/components/SqlDetail.tsx | 38 +-- .../Datasource/components/SqlSide.tsx | 5 +- .../pages/SemanticModel/Datasource/index.tsx | 9 +- .../src/pages/SemanticModel/DomainManager.tsx | 22 ++ .../Metric/components/MetricFilter.tsx | 4 +- .../src/pages/SemanticModel/Metric/index.tsx | 82 ++++- .../pages/SemanticModel/OverviewContainer.tsx | 290 ++++++++++++++++++ .../pages/SemanticModel/ProjectManager.tsx | 251 --------------- .../SemanticModel/SemanticFlows/service.ts | 11 +- .../components/DeleteConfirmModal.tsx | 2 +- .../SemanticModel/SemanticGraph/index.tsx | 37 ++- .../SemanticModel/SemanticGraphCanvas.tsx | 2 +- .../components/BindMeasuresTable.tsx | 1 + .../components/ClassDataSourceTable.tsx | 8 +- .../components/ClassDataSourceTypeModal.tsx | 5 +- .../components/ClassDataSourceTypeModal1.tsx | 101 ------ .../components/ClassDimensionTable.tsx | 21 +- .../components/ClassMetricTable.tsx | 20 +- .../Database/DatabaseCreateForm.tsx | 2 +- .../components/DimensionInfoModal.tsx | 6 +- .../SemanticModel/components/DomainList.tsx | 14 +- .../components/DomainManagerTab.tsx | 135 ++++++++ .../components/Entity/DefaultSettingForm.tsx | 2 +- .../Entity/DimensionAndMetricVisibleModal.tsx | 2 +- .../components/Entity/EntityCreateForm.tsx | 35 +-- .../components/Entity/EntitySection.tsx | 17 +- .../Entity/EntitySettingSection.tsx | 18 +- .../Entity/RecommendedQuestionsSection.tsx | 11 +- .../components/MetricInfoCreateForm.tsx | 11 +- .../components/ModelCreateFormModal.tsx | 95 ++++++ .../SemanticModel/components/OverView.tsx | 119 +++++-- .../Permission/PermissionAdminForm.tsx | 37 ++- .../Permission/PermissionCreateDrawer.tsx | 12 +- .../Permission/PermissionCreateForm.tsx | 1 - .../Permission/PermissionSection.tsx | 8 +- .../components/Permission/PermissionTable.tsx | 15 +- .../components/ProjectInfoForm.tsx | 18 +- .../src/pages/SemanticModel/data.d.ts | 24 ++ .../src/pages/SemanticModel/model.ts | 15 + .../src/pages/SemanticModel/service.ts | 72 ++++- .../supersonic-fe/src/services/API.d.ts | 6 +- 48 files changed, 1104 insertions(+), 863 deletions(-) create mode 100644 package-lock.json delete mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting/ChatSetting.tsx create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting/ChatSettingTab.tsx create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/DomainManager.tsx create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/OverviewContainer.tsx delete mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx delete mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal1.tsx create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DomainManagerTab.tsx create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ModelCreateFormModal.tsx diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..7cd3ea7bc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "github-supersonic", + "lockfileVersion": 2, + "requires": true, + "packages": {} +} diff --git a/webapp/packages/supersonic-fe/config/routes.ts b/webapp/packages/supersonic-fe/config/routes.ts index af5055727..a6cab8a4a 100644 --- a/webapp/packages/supersonic-fe/config/routes.ts +++ b/webapp/packages/supersonic-fe/config/routes.ts @@ -15,9 +15,9 @@ const ROUTES = [ envEnableList: [ENV_KEY.CHAT], }, { - path: '/chatSetting/:modelId?/:menuKey?', + path: '/chatSetting/model/:domainId?/:modelId?/:menuKey?', + component: './SemanticModel/ChatSetting/ChatSetting', name: 'chatSetting', - component: './SemanticModel/ChatSetting', envEnableList: [ENV_KEY.CHAT], }, { @@ -27,9 +27,9 @@ const ROUTES = [ envEnableList: [ENV_KEY.CHAT], }, { - path: '/semanticModel/:modelId?/:menuKey?', + path: '/semanticModel/model/:domainId?/:modelId?/:menuKey?', + component: './SemanticModel/DomainManager', name: 'semanticModel', - component: './SemanticModel/ProjectManager', envEnableList: [ENV_KEY.SEMANTIC], }, { @@ -47,10 +47,10 @@ const ROUTES = [ }, { path: '/', - redirect: APP_TARGET === 'inner' ? '/semanticModel' : '/chat', + redirect: APP_TARGET === 'inner' ? '/semanticModel/model/' : '/chat', envRedirect: { [ENV_KEY.CHAT]: '/chat', - [ENV_KEY.SEMANTIC]: '/semanticModel', + [ENV_KEY.SEMANTIC]: '/semanticModel/model', }, }, { diff --git a/webapp/packages/supersonic-fe/src/enum/models/base.ts b/webapp/packages/supersonic-fe/src/enum/models/base.ts index 47ce44df6..48dc7d911 100644 --- a/webapp/packages/supersonic-fe/src/enum/models/base.ts +++ b/webapp/packages/supersonic-fe/src/enum/models/base.ts @@ -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 = { edit: '编辑', add: '新增', @@ -29,20 +17,3 @@ export const EnumDescSensitivity = { 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, // 看板 -} diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx deleted file mode 100644 index 0bf1f3ab4..000000000 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx +++ /dev/null @@ -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 = ({ 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([]); - const [open, setOpen] = useState(false); - const [isModel, setIsModel] = useState(false); - const [activeKey, setActiveKey] = useState(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: , - }, - ]; - - const isModelItem = [ - { - label: '指标模式', - key: 'metric', - children: , - }, - { - label: '实体模式', - key: 'dimenstion', - children: , - }, - { - label: '推荐问题', - key: 'recommendedQuestions', - children: , - }, - ]; - - return ( -
- -
-

- { - setOpen(false); - }} - /> - } - trigger="click" - open={open} - onOpenChange={handleOpenChange} - > -
- - {selectDomainName ? `选择的主题域:${selectDomainName}` : '主题域信息'} - - - - -
-
-

- {selectDomainId ? ( - <> - { - setActiveKey(menuKey); - pushUrlMenu(selectDomainId, menuKey); - }} - items={!isModel ? tabItem : isModelItem} - /> - - ) : ( -

请选择项目

- )} -
-
- ); -}; - -export default connect(({ domainManger }: { domainManger: StateType }) => ({ - domainManger, -}))(ChatSetting); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting/ChatSetting.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting/ChatSetting.tsx new file mode 100644 index 000000000..1f656b49f --- /dev/null +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting/ChatSetting.tsx @@ -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 = () => { + return ( + <> + + + + ); +}; + +export default connect(({ domainManger }: { domainManger: StateType }) => ({ + domainManger, +}))(ChatSetting); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting/ChatSettingTab.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting/ChatSettingTab.tsx new file mode 100644 index 000000000..b2008fb81 --- /dev/null +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting/ChatSettingTab.tsx @@ -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 = ({ + isModel, + activeKey, + modelList, + handleModelChange, + onBackDomainBtnClick, + onMenuChange, +}) => { + const defaultTabKey = 'metric'; + + const isModelItem = [ + { + label: '指标模式', + key: 'metric', + children: , + }, + { + label: '实体模式', + key: 'dimenstion', + children: , + }, + { + label: '推荐问题', + key: 'recommendedQuestions', + children: , + }, + ]; + + const tabItem = [ + { + label: '模型', + key: 'overview', + children: ( + { + handleModelChange(model); + }} + /> + ), + }, + ]; + + return ( + <> + } + onClick={() => { + onBackDomainBtnClick?.(); + }} + style={{ marginRight: 10 }} + > + 返回主题域 + + ) : undefined + } + onChange={(menuKey: string) => { + onMenuChange?.(menuKey); + }} + /> + + ); +}; + +export default connect(({ domainManger }: { domainManger: StateType }) => ({ + domainManger, +}))(ChatSetting); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceCreateForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceCreateForm.tsx index 4b41a4b35..6b1e7d343 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceCreateForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/DataSourceCreateForm.tsx @@ -10,14 +10,12 @@ import { createDatasource, updateDatasource, getColumns } from '../../service'; import type { Dispatch } from 'umi'; import type { StateType } from '../../model'; import { connect } from 'umi'; -import { isUndefined } from 'lodash'; export type CreateFormProps = { domainManger: StateType; dispatch: Dispatch; createModalVisible: boolean; sql?: string; - domainId: number; dataSourceItem: DataInstanceItem | any; onCancel?: () => void; onSubmit?: (dataSourceInfo: any) => void; @@ -37,7 +35,6 @@ const DataSourceCreateForm: React.FC = ({ domainManger, onCancel, createModalVisible, - domainId, scriptColumns, sql = '', onSubmit, @@ -51,7 +48,7 @@ const DataSourceCreateForm: React.FC = ({ const [hasEmptyNameField, setHasEmptyNameField] = useState(false); const formValRef = useRef(initFormVal as any); const [form] = Form.useForm(); - const { dataBaseConfig } = domainManger; + const { dataBaseConfig, selectModelId: modelId } = domainManger; const updateFormVal = (val: any) => { formValRef.current = val; }; @@ -163,7 +160,7 @@ const DataSourceCreateForm: React.FC = ({ databaseId: dataSourceItem?.databaseId || dataBaseConfig.id, queryType: basicInfoFormMode === 'fast' ? 'table_query' : 'sql_query', tableQuery: dbName && tableName ? `${dbName}.${tableName}` : '', - domainId, + modelId, }; const queryDatasource = isEdit ? updateDatasource : createDatasource; const { code, msg, data } = await queryDatasource(queryParams); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx index 72334174f..cab91d9dd 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx @@ -18,7 +18,6 @@ import FullScreen from '@/components/FullScreen'; import SqlEditor from '@/components/SqlEditor'; import type { TaskResultItem, DataInstanceItem, TaskResultColumn } from '../data'; import { excuteSql } from '@/pages/SemanticModel/service'; -// import { getDatabaseByDomainId } from '../../service'; import DataSourceCreateForm from './DataSourceCreateForm'; import type { Dispatch } from 'umi'; import type { StateType } from '../../model'; @@ -33,7 +32,6 @@ type IProps = { domainManger: StateType; dispatch: Dispatch; dataSourceItem: DataInstanceItem; - domainId: number; onUpdateSql?: (sql: string) => void; sql?: string; onSubmitSuccess?: (dataSourceInfo: any) => void; @@ -59,12 +57,11 @@ const SqlDetail: React.FC = ({ domainManger, dataSourceItem, onSubmitSuccess, - domainId, sql = '', onUpdateSql, onJdbcSourceChange, }) => { - const { dataBaseConfig } = domainManger; + const { dataBaseConfig, selectModelId: modelId } = domainManger; const [resultTable, setResultTable] = useState([]); const [resultTableLoading, setResultTableLoading] = useState(false); const [resultCols, setResultCols] = useState([]); @@ -82,8 +79,6 @@ const SqlDetail: React.FC = ({ y: 200, }); - // const [dataSourceResult, setDataSourceResult] = useState({}); - const [runState, setRunState] = useState(); const [taskLog, setTaskLog] = useState(''); @@ -93,7 +88,6 @@ const SqlDetail: React.FC = ({ const [isSqlIdeFullScreen, setIsSqlIdeFullScreen] = useState(false); const [isSqlResFullScreen, setIsSqlResFullScreen] = useState(false); - // const [sqlParams, setSqlParams] = useState([]); const resultInnerWrap = useRef(); const [editorSize, setEditorSize] = useState(0); @@ -104,18 +98,6 @@ const SqlDetail: React.FC = ({ const [isRight, setIsRight] = useState(false); const [scriptColumns, setScriptColumns] = useState([]); - // const [jdbcSourceName, setJdbcSourceName] = useState(() => { - // 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(() => { setJdbcSourceItems([ @@ -127,21 +109,6 @@ const SqlDetail: React.FC = ({ onJdbcSourceChange?.(dataBaseConfig?.id && Number(dataBaseConfig?.id)); }, [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) { const line = document.createElement('div'); // 需要每条数据一行,这样避免数据换行的时候获得的宽度不准确 const child = document.createElement('span'); @@ -245,7 +212,7 @@ const SqlDetail: React.FC = ({ setResultTableLoading(true); const { code, data, msg } = await excuteSql({ sql: value, - domainId, + modelId, }); setResultTableLoading(false); if (code === 200) { @@ -521,7 +488,6 @@ const SqlDetail: React.FC = ({ {dataSourceModalVisible && ( { diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlSide.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlSide.tsx index 57575e397..333953667 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlSide.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlSide.tsx @@ -22,14 +22,13 @@ type TableRef = { type Props = { initialValues: any; - domainId: number; onSubmitSuccess?: (dataSourceInfo: any) => void; }; const { TabPane } = Tabs; const LIST_KEY = 'list'; -const SqlSide: React.FC = ({ initialValues, domainId, onSubmitSuccess }) => { +const SqlSide: React.FC = ({ initialValues, onSubmitSuccess }) => { const defaultPanes: Panes[] = [ { key: '数据源查询', @@ -98,8 +97,6 @@ const SqlSide: React.FC = ({ initialValues, domainId, onSubmitSuccess }) { updateTabSql(sql, pane.key); }} diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/index.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/index.tsx index 018da26b0..cc4c353fc 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/index.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/index.tsx @@ -7,13 +7,12 @@ import { RightOutlined, LeftOutlined } from '@ant-design/icons'; type Props = { initialValues: any; - domainId: number; onSubmitSuccess?: (dataSourceInfo: any) => void; }; const DEFAULT_RIGHT_SIZE = '300px'; -const DataExploreView: React.FC = ({ initialValues, domainId, onSubmitSuccess }) => { +const DataExploreView: React.FC = ({ initialValues, onSubmitSuccess }) => { const [collapsed, setCollapsed] = useState(false); useEffect(() => { @@ -51,11 +50,7 @@ const DataExploreView: React.FC = ({ initialValues, domainId, onSubmitSuc {collapsed ? : } )} - + diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/DomainManager.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/DomainManager.tsx new file mode 100644 index 000000000..11d0127c7 --- /dev/null +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/DomainManager.tsx @@ -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 = () => { + return ( + <> + + + + ); +}; + +export default connect(({ domainManger }: { domainManger: StateType }) => ({ + domainManger, +}))(DomainManager); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx index ee6f77d13..1d30c2b11 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx @@ -1,4 +1,4 @@ -import { Form, Select, Input } from 'antd'; +import { Form, Input } from 'antd'; import StandardFormRow from '@/components/StandardFormRow'; import TagSelect from '@/components/TagSelect'; import React, { useEffect } from 'react'; @@ -64,7 +64,7 @@ const MetricFilter: React.FC = ({ filterValues = {}, onFiltersChange }) = >
- +
= () => { +const ClassMetricTable: React.FC = ({ domainManger, dispatch }) => { + const { selectDomainId } = domainManger; + const [createModalVisible, setCreateModalVisible] = useState(false); const [pagination, setPagination] = useState({ current: 1, pageSize: 20, total: 0, }); const [loading, setLoading] = useState(false); - const [dataSource, setDataSource] = useState([]); + const [dataSource, setDataSource] = useState([]); + const [metricItem, setMetricItem] = useState(); const [filterParams, setFilterParams] = useState>({}); const actionRef = useRef(); @@ -92,8 +96,8 @@ const ClassMetricTable: React.FC = () => { title: '字段名称', }, { - dataIndex: 'domainName', - title: '主题域', + dataIndex: 'modelName', + title: '所属模型', }, { dataIndex: 'sensitiveLevel', @@ -127,6 +131,50 @@ const ClassMetricTable: React.FC = () => { return value && value !== '-' ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-'; }, }, + { + title: '操作', + dataIndex: 'x', + valueType: 'option', + render: (_, record) => { + return ( + + { + setMetricItem(record); + setCreateModalVisible(true); + }} + > + 编辑 + + + { + const { code, msg } = await deleteMetric(record.id); + if (code === 200) { + setMetricItem(undefined); + actionRef.current?.reload(); + } else { + message.error(msg); + } + }} + > + { + setMetricItem(record); + }} + > + 删除 + + + + ); + }, + }, ]; const handleFilterChange = async (filterParams: { @@ -179,6 +227,26 @@ const ClassMetricTable: React.FC = () => { size="small" options={{ reload: false, density: false, fullScreen: false }} /> + {createModalVisible && ( + { + setCreateModalVisible(false); + actionRef?.current?.reload(); + dispatch({ + type: 'domainManger/queryMetricList', + payload: { + domainId: selectDomainId, + }, + }); + }} + onCancel={() => { + setCreateModalVisible(false); + }} + /> + )} ); }; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/OverviewContainer.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/OverviewContainer.tsx new file mode 100644 index 000000000..f67dac868 --- /dev/null +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/OverviewContainer.tsx @@ -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 = ({ 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([]); + const [isModel, setIsModel] = useState(false); + const [open, setOpen] = useState(false); + const [activeKey, setActiveKey] = useState(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 ( +
+ +
+

+ { + 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} + > +
+ + + {selectDomainName ? `当前主题域:${selectDomainName}` : '主题域信息'} + {selectModelName && ( + <> + | + {selectModelName} + + )} + + + + + +
+
+

+ + {selectDomainId ? ( + <> + {mode === 'domain' ? ( + { + handleModelChange(model); + }} + onBackDomainBtnClick={() => { + cleanModelInfo(selectDomainId); + }} + onMenuChange={(menuKey) => { + setActiveKey(menuKey); + pushUrlMenu(selectDomainId, selectModelId, menuKey); + }} + /> + ) : ( + { + handleModelChange(model); + }} + onBackDomainBtnClick={() => { + cleanModelInfo(selectDomainId); + }} + onMenuChange={(menuKey) => { + setActiveKey(menuKey); + pushUrlMenu(selectDomainId, selectModelId, menuKey); + }} + /> + )} + + ) : ( +

请选择项目

+ )} +
+
+ ); +}; + +export default connect(({ domainManger }: { domainManger: StateType }) => ({ + domainManger, +}))(OverviewContainer); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx deleted file mode 100644 index 8feacfc92..000000000 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx +++ /dev/null @@ -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 = ({ 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([]); - const [isModel, setIsModel] = useState(false); - const [open, setOpen] = useState(false); - const [activeKey, setActiveKey] = useState(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: , - }, - { - label: '权限管理', - key: 'permissonSetting', - children: , - }, - ]; - - const isModelItem = [ - { - label: '画布', - key: 'xflow', - children: ( -
- -
- ), - }, - { - label: '数据库', - key: 'dataBase', - children: , - }, - { - label: '数据源', - key: 'dataSource', - children: , - }, - { - label: '维度', - key: 'dimenstion', - children: , - }, - { - label: '指标', - key: 'metric', - children: , - }, - { - label: '实体', - key: 'entity', - children: , - }, - - { - label: '权限管理', - key: 'permissonSetting', - children: , - }, - ]; - - return ( -
- -
-

- { - setOpen(false); - }} - onTreeDataUpdate={() => { - initProjectTree(); - }} - /> - } - trigger="click" - open={open} - onOpenChange={handleOpenChange} - > -
- - {selectDomainName ? `当前主题域:${selectDomainName}` : '主题域信息'} - - - - -
-
-

- {selectDomainId ? ( - <> - { - setActiveKey(menuKey); - pushUrlMenu(selectDomainId, menuKey); - }} - /> - - ) : ( -

请选择项目

- )} -
-
- ); -}; - -export default connect(({ domainManger }: { domainManger: StateType }) => ({ - domainManger, -}))(DomainManger); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticFlows/service.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticFlows/service.ts index a3d5f0b1f..a3d0d9656 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticFlows/service.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticFlows/service.ts @@ -13,7 +13,6 @@ import { deleteDatasource, getDimensionList, createOrUpdateViewInfo, - getViewInfoList, deleteDatasourceRela, } from '../service'; import { message } from 'antd'; @@ -85,8 +84,8 @@ export namespace GraphApi { export const loadDataSourceData = async (args: NsGraph.IGraphMeta) => { const { domainManger, graphConfig } = args.meta; - const { selectDomainId } = domainManger; - const { code, data = [] } = await getDatasourceList({ domainId: selectDomainId }); + const { selectModelId } = domainManger; + const { code, data = [] } = await getDatasourceList({ modelId: selectModelId }); const dataSourceMap = data.reduce( (itemMap: Record, item: IDataSource.IDataSourceItem) => { const { id, name } = item; @@ -161,8 +160,8 @@ export namespace GraphApi { export const loadDimensionData = async (args: NsGraph.IGraphMeta) => { const { domainManger } = args.meta; - const { domainId } = domainManger; - const { code, data } = await getDimensionList({ domainId }); + const { selectModelId } = domainManger; + const { code, data } = await getDimensionList({ modelId: selectModelId }); if (code === 200) { const { list } = data; const nodes: NsGraph.INodeConfig[] = list.map((item: any) => { @@ -210,7 +209,7 @@ export namespace GraphApi { const { domainManger, graphConfig } = graphMeta.meta; const { code, msg } = await createOrUpdateViewInfo({ id: graphConfig?.id, - domainId: domainManger.selectDomainId, + modelId: domainManger.selectModelId, type: 'datasource', config: JSON.stringify(tempGraphData), }); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/DeleteConfirmModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/DeleteConfirmModal.tsx index 02628b306..b9ad69110 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/DeleteConfirmModal.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/DeleteConfirmModal.tsx @@ -18,7 +18,6 @@ const DeleteConfirmModal: React.FC = ({ }) => { const [confirmLoading, setConfirmLoading] = useState(false); const deleteNode = async () => { - setConfirmLoading(true); const { id, nodeType } = nodeData; let deleteQuery; if (nodeType === SemanticNodeType.DIMENSION) { @@ -34,6 +33,7 @@ const DeleteConfirmModal: React.FC = ({ message.error('当前节点类型不是维度,指标,数据源中的一种,请确认节点数据'); return; } + setConfirmLoading(true); const { code, msg } = await deleteQuery(id); setConfirmLoading(false); if (code === 200) { diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx index 49911df5f..c1d476df9 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx @@ -32,7 +32,6 @@ import GraphLegendVisibleModeItem from './components/GraphLegendVisibleModeItem' // import { cloneDeep } from 'lodash'; type Props = { - domainId: number; // graphShowType?: SemanticNodeType; domainManger: StateType; dispatch: Dispatch; @@ -40,7 +39,6 @@ type Props = { const DomainManger: React.FC = ({ domainManger, - domainId, // graphShowType = SemanticNodeType.DIMENSION, // graphShowType, dispatch, @@ -65,7 +63,7 @@ const DomainManger: React.FC = ({ const [dataSourceInfoList, setDataSourceInfoList] = useState([]); - const { dimensionList, metricList } = domainManger; + const { dimensionList, metricList, selectModelId: modelId, selectDomainId } = domainManger; const dimensionListRef = useRef([]); const metricListRef = useRef([]); @@ -139,10 +137,10 @@ const DomainManger: React.FC = ({ }; const queryDataSourceList = async (params: { - domainId: number; + modelId: number; graphShowType?: SemanticNodeType; }) => { - const { code, data } = await getDomainSchemaRela(params.domainId); + const { code, data } = await getDomainSchemaRela(params.modelId); if (code === 200) { if (data) { setDataSourceInfoList( @@ -165,8 +163,8 @@ const DomainManger: React.FC = ({ useEffect(() => { graphLegendDataSourceIds.current = undefined; graphRef.current = null; - queryDataSourceList({ domainId }); - }, [domainId]); + queryDataSourceList({ modelId }); + }, [modelId]); // const getLegendDataFilterFunctions = () => { // legendDataRef.current.map((item: any) => { @@ -273,7 +271,7 @@ const DomainManger: React.FC = ({ if (targetData.nodeType === SemanticNodeType.DIMENSION) { const targetItem = dimensionListRef.current.find((item) => item.id === targetData.uid); if (targetItem) { - setCurrentNodeData(targetItem); + setCurrentNodeData({ ...targetData, ...targetItem }); setConfirmModalOpenState(true); } else { message.error('获取维度初始化数据失败'); @@ -282,7 +280,7 @@ const DomainManger: React.FC = ({ if (targetData.nodeType === SemanticNodeType.METRIC) { const targetItem = metricListRef.current.find((item) => item.id === targetData.uid); if (targetItem) { - setCurrentNodeData(targetItem); + setCurrentNodeData({ ...targetData, ...targetItem }); setConfirmModalOpenState(true); } else { message.error('获取指标初始化数据失败'); @@ -515,7 +513,7 @@ const DomainManger: React.FC = ({ const updateGraphData = async (params?: { graphShowType?: SemanticNodeType }) => { const graphRootData = await queryDataSourceList({ - domainId, + modelId, graphShowType: params?.graphShowType, }); if (graphRootData) { @@ -571,7 +569,7 @@ const DomainManger: React.FC = ({ />
@@ -595,7 +593,7 @@ const DomainManger: React.FC = ({ dispatch({ type: 'domainManger/queryMetricList', payload: { - domainId, + modelId, }, }); } @@ -603,7 +601,7 @@ const DomainManger: React.FC = ({ dispatch({ type: 'domainManger/queryDimensionList', payload: { - domainId, + modelId, }, }); } @@ -612,7 +610,7 @@ const DomainManger: React.FC = ({ {createDimensionModalVisible && ( = ({ dispatch({ type: 'domainManger/queryDimensionList', payload: { - domainId, + modelId, }, }); }} @@ -633,7 +631,8 @@ const DomainManger: React.FC = ({ )} {createMetricModalVisible && ( = ({ dispatch({ type: 'domainManger/queryMetricList', payload: { - domainId, + modelId, }, }); }} @@ -676,13 +675,13 @@ const DomainManger: React.FC = ({ ? dispatch({ type: 'domainManger/queryDimensionList', payload: { - domainId, + modelId, }, }) : dispatch({ type: 'domainManger/queryMetricList', payload: { - domainId, + modelId, }, }); }} diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx index 4fc864a0a..a7c6e2a83 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx @@ -38,7 +38,7 @@ const SemanticGraphCanvas: React.FC = ({ domainManger }) => {
) : ( */}
- +
{/* )} */}
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/BindMeasuresTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/BindMeasuresTable.tsx index 90f84913d..fd44553be 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/BindMeasuresTable.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/BindMeasuresTable.tsx @@ -113,6 +113,7 @@ const BindMeasuresTable: React.FC = ({ size="small" search={false} options={false} + scroll={{ y: 800 }} /> ); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTable.tsx index e12aac195..c4e3cebae 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTable.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTable.tsx @@ -15,7 +15,7 @@ type Props = { }; const ClassDataSourceTable: React.FC = ({ dispatch, domainManger }) => { - const { selectDomainId } = domainManger; + const { selectModelId } = domainManger; const [dataSourceItem, setDataSourceItem] = useState(); const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false); @@ -59,7 +59,7 @@ const ClassDataSourceTable: React.FC = ({ dispatch, domainManger }) => { return ( { setDataSourceItem(record); setCreateDataSourceModalOpen(true); @@ -82,7 +82,7 @@ const ClassDataSourceTable: React.FC = ({ dispatch, domainManger }) => { }} > { setDataSourceItem(record); }} @@ -127,7 +127,7 @@ const ClassDataSourceTable: React.FC = ({ dispatch, domainManger }) => { actionRef={actionRef} rowKey="id" columns={columns} - params={{ domainId: selectDomainId }} + params={{ modelId: selectModelId }} request={queryDataSourceList} pagination={false} search={false} diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal.tsx index 597382cad..8860865b2 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal.tsx @@ -28,7 +28,7 @@ const ClassDataSourceTypeModal: React.FC = ({ onCancel, dispatch, }) => { - const { selectDomainId, dataBaseConfig } = domainManger; + const { selectDomainId, dataBaseConfig, selectModelId } = domainManger; const [createModalVisible, setCreateModalVisible] = useState(false); const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false); @@ -128,7 +128,7 @@ const ClassDataSourceTypeModal: React.FC = ({ type="primary" key="console" onClick={() => { - history.replace(`/semanticModel/${selectDomainId}/dataBase`); + history.replace(`/semanticModel/${selectDomainId}/0/dataBase`); onCancel?.(); }} > @@ -173,7 +173,6 @@ const ClassDataSourceTypeModal: React.FC = ({ > { setCreateModalVisible(false); onSubmit?.(); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal1.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal1.tsx deleted file mode 100644 index 7632e1cff..000000000 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal1.tsx +++ /dev/null @@ -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 = ({ - open, - onTypeChange, - domainManger, - onCancel, -}) => { - const { selectDomainId, dataBaseConfig } = domainManger; - const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false); - useEffect(() => { - setCreateDataSourceModalOpen(open); - }, [open]); - - return ( - <> - { - setCreateDataSourceModalOpen(false); - onCancel?.(); - }} - footer={null} - centered - closable={false} - > - {dataBaseConfig && dataBaseConfig.id ? ( - - - { - onTypeChange('fast'); - setCreateDataSourceModalOpen(false); - }} - cover={ - - } - > - - - - - { - onTypeChange('normal'); - setCreateDataSourceModalOpen(false); - }} - hoverable - style={{ height: 220 }} - cover={ - - } - > - - - - - ) : ( - { - history.replace(`/semanticModel/${selectDomainId}/dataBase`); - onCancel?.(); - }} - > - 去设置 - - } - /> - )} - - - ); -}; - -export default connect(({ domainManger }: { domainManger: StateType }) => ({ - domainManger, -}))(ClassDataSourceTypeModal); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx index 856531da8..4deb6ad87 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx @@ -19,7 +19,7 @@ type Props = { }; const ClassDimensionTable: React.FC = ({ domainManger, dispatch }) => { - const { selectDomainId } = domainManger; + const { selectModelId: modelId } = domainManger; const [createModalVisible, setCreateModalVisible] = useState(false); const [dimensionItem, setDimensionItem] = useState(); const [dataSourceList, setDataSourceList] = useState([]); @@ -40,7 +40,7 @@ const ClassDimensionTable: React.FC = ({ domainManger, dispatch }) => { const { code, data, msg } = await getDimensionList({ ...params, ...pagination, - domainId: selectDomainId, + modelId, }); const { list, pageSize, current, total } = data || {}; let resData: any = {}; @@ -67,7 +67,7 @@ const ClassDimensionTable: React.FC = ({ domainManger, dispatch }) => { }; const queryDataSourceList = async () => { - const { code, data, msg } = await getDatasourceList({ domainId: selectDomainId }); + const { code, data, msg } = await getDatasourceList({ modelId }); if (code === 200) { setDataSourceList(data); } else { @@ -77,7 +77,7 @@ const ClassDimensionTable: React.FC = ({ domainManger, dispatch }) => { useEffect(() => { queryDataSourceList(); - }, [selectDomainId]); + }, [modelId]); const columns: ProColumns[] = [ { @@ -139,7 +139,7 @@ const ClassDimensionTable: React.FC = ({ domainManger, dispatch }) => { return ( { setDimensionItem(record); setCreateModalVisible(true); @@ -148,7 +148,7 @@ const ClassDimensionTable: React.FC = ({ domainManger, dispatch }) => { 编辑 { setDimensionItem(record); setDimensionValueSettingModalVisible(true); @@ -176,7 +176,7 @@ const ClassDimensionTable: React.FC = ({ domainManger, dispatch }) => { }} > { setDimensionItem(record); }} @@ -195,7 +195,6 @@ const ClassDimensionTable: React.FC = ({ domainManger, dispatch }) => { = ({ domainManger, dispatch }) => { {createModalVisible && ( = ({ domainManger, dispatch }) => { dispatch({ type: 'domainManger/queryDimensionList', payload: { - domainId: selectDomainId, + modelId, }, }); return; @@ -269,7 +268,7 @@ const ClassDimensionTable: React.FC = ({ domainManger, dispatch }) => { dispatch({ type: 'domainManger/queryDimensionList', payload: { - domainId: selectDomainId, + modelId, }, }); setDimensionValueSettingModalVisible(false); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassMetricTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassMetricTable.tsx index c6b9b5cd2..933e2fe60 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassMetricTable.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassMetricTable.tsx @@ -12,6 +12,7 @@ import MetricInfoCreateForm from './MetricInfoCreateForm'; import moment from 'moment'; import styles from './style.less'; +import { ISemantic } from '../data'; type Props = { dispatch: Dispatch; @@ -19,9 +20,9 @@ type Props = { }; const ClassMetricTable: React.FC = ({ domainManger, dispatch }) => { - const { selectDomainId } = domainManger; + const { selectModelId: modelId, selectDomainId } = domainManger; const [createModalVisible, setCreateModalVisible] = useState(false); - const [metricItem, setMetricItem] = useState(); + const [metricItem, setMetricItem] = useState(); const [pagination, setPagination] = useState({ current: 1, pageSize: 20, @@ -33,7 +34,7 @@ const ClassMetricTable: React.FC = ({ domainManger, dispatch }) => { const { code, data, msg } = await queryMetric({ ...params, ...pagination, - domainId: selectDomainId, + modelId, }); const { list, pageSize, current, total } = data || {}; let resData: any = {}; @@ -95,7 +96,6 @@ const ClassMetricTable: React.FC = ({ domainManger, dispatch }) => { { dataIndex: 'type', title: '指标类型', - // search: false, valueEnum: { ATOMIC: '原子指标', DERIVED: '衍生指标', @@ -128,7 +128,7 @@ const ClassMetricTable: React.FC = ({ domainManger, dispatch }) => { return ( { setMetricItem(record); setCreateModalVisible(true); @@ -152,7 +152,7 @@ const ClassMetricTable: React.FC = ({ domainManger, dispatch }) => { }} > { setMetricItem(record); }} @@ -171,7 +171,6 @@ const ClassMetricTable: React.FC = ({ domainManger, dispatch }) => { = ({ domainManger, dispatch }) => { }, }} columns={columns} - params={{ domainId: selectDomainId }} + params={{ modelId }} request={queryMetricList} pagination={pagination} tableAlertRender={() => { @@ -212,7 +211,8 @@ const ClassMetricTable: React.FC = ({ domainManger, dispatch }) => { /> {createModalVisible && ( { @@ -221,7 +221,7 @@ const ClassMetricTable: React.FC = ({ domainManger, dispatch }) => { dispatch({ type: 'domainManger/queryMetricList', payload: { - domainId: selectDomainId, + modelId, }, }); }} diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Database/DatabaseCreateForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Database/DatabaseCreateForm.tsx index 803b947fe..e516eacba 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Database/DatabaseCreateForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Database/DatabaseCreateForm.tsx @@ -1,7 +1,7 @@ import { useEffect, forwardRef, useImperativeHandle, useState } from 'react'; import type { ForwardRefRenderFunction } from 'react'; 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 styles from '../style.less'; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionInfoModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionInfoModal.tsx index 9e95fc1f6..b37e57967 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionInfoModal.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionInfoModal.tsx @@ -10,7 +10,7 @@ import { createDimension, updateDimension } from '../service'; import { message } from 'antd'; export type CreateFormProps = { - domainId: number; + modelId: number; dimensionItem?: ISemantic.IDimensionItem; onCancel: () => void; bindModalVisible: boolean; @@ -24,7 +24,7 @@ const { Option } = Select; const { TextArea } = Input; const DimensionInfoModal: React.FC = ({ - domainId, + modelId, onCancel, bindModalVisible, dimensionItem, @@ -55,7 +55,7 @@ const DimensionInfoModal: React.FC = ({ const saveDimension = async (fieldsValue: any, isSilenceSubmit = false) => { const queryParams = { - domainId, + modelId, type: 'categorical', ...fieldsValue, }; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DomainList.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DomainList.tsx index c9792fec4..71e4401be 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DomainList.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DomainList.tsx @@ -24,7 +24,7 @@ type DomainListProps = { createDomainBtnVisible?: boolean; dispatch: Dispatch; onCreateDomainBtnClick?: () => void; - onTreeSelected?: () => void; + onTreeSelected?: (targetNodeData: ISemantic.IDomainItem) => void; onTreeDataUpdate?: () => void; }; @@ -57,7 +57,7 @@ const DomainListTree: FC = ({ const [projectInfoParams, setProjectInfoParams] = useState({}); const [filterValue, setFliterValue] = useState(''); const [expandedKeys, setExpandedKeys] = useState([]); - const [classList, setClassList] = useState([]); + const [classList, setClassList] = useState([]); useEffect(() => { const treeData = addPathInTreeData(constructorClassTreeFromList(domainList)); @@ -77,13 +77,7 @@ const DomainListTree: FC = ({ const targetNodeData = classList.filter((item: any) => { return item.id === selectedKeys; })[0]; - onTreeSelected?.(); - dispatch({ - type: 'domainManger/setSelectDomain', - selectDomainId: selectedKeys, - selectDomainName: projectName, - domainData: targetNodeData, - }); + onTreeSelected?.(targetNodeData); }; const editProject = async (values: any) => { @@ -134,7 +128,7 @@ const DomainListTree: FC = ({ {createDomainBtnVisible && ( - {Array.isArray(path) && path.length < 3 && ( + {Array.isArray(path) && path.length < 2 && ( { diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DomainManagerTab.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DomainManagerTab.tsx new file mode 100644 index 000000000..529634dbb --- /dev/null +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DomainManagerTab.tsx @@ -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 = ({ + isModel, + activeKey, + modelList, + handleModelChange, + onBackDomainBtnClick, + onMenuChange, +}) => { + const defaultTabKey = 'xflow'; + + const tabItem = [ + { + label: '模型', + key: 'overview', + children: ( + { + handleModelChange(model); + }} + /> + ), + }, + { + label: '数据库', + key: 'dataBase', + children: , + }, + { + label: '权限管理', + key: 'permissonSetting', + children: , + }, + ]; + + const isModelItem = [ + { + label: '画布', + key: 'xflow', + children: ( +
+ +
+ ), + }, + + { + label: '数据源', + key: 'dataSource', + children: , + }, + { + label: '维度', + key: 'dimenstion', + children: , + }, + { + label: '指标', + key: 'metric', + children: , + }, + { + label: '实体', + key: 'entity', + children: , + }, + + { + label: '权限管理', + key: 'permissonSetting', + children: , + }, + ]; + + return ( + <> + } + onClick={() => { + onBackDomainBtnClick?.(); + }} + style={{ marginRight: 10 }} + > + 返回主题域 + + ) : undefined + } + onChange={(menuKey: string) => { + onMenuChange?.(menuKey); + }} + /> + + ); +}; + +export default connect(({ domainManger }: { domainManger: StateType }) => ({ + domainManger, +}))(DomainManagerTab); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DefaultSettingForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DefaultSettingForm.tsx index c1659473f..c8b1cedc8 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DefaultSettingForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DefaultSettingForm.tsx @@ -143,7 +143,7 @@ const DefaultSettingForm: ForwardRefRenderFunction = ( }; const { code, msg, data } = await saveDomainExtendQuery({ [chatConfigKey]: params, - domainId, + // domainId, id, }); if (code === 200) { diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionAndMetricVisibleModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionAndMetricVisibleModal.tsx index 098f4e73b..996c3518d 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionAndMetricVisibleModal.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionAndMetricVisibleModal.tsx @@ -126,7 +126,7 @@ const DimensionAndMetricVisibleModal: React.FC = ({ const { code, msg } = await saveDomainExtendQuery({ [chatConfigKey]: params, - domainId, + // domainId, id, }); if (code === 200) { diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntityCreateForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntityCreateForm.tsx index 343a9f41a..292571708 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntityCreateForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntityCreateForm.tsx @@ -1,22 +1,22 @@ import { useEffect, useState, forwardRef, useImperativeHandle } from 'react'; import type { ForwardRefRenderFunction } from 'react'; import { message, Form, Input, Select, Button } from 'antd'; -import { updateDomain } from '../../service'; +import { updateModel } from '../../service'; import type { ISemantic } from '../../data'; import { formLayout } from '@/components/FormHelper/utils'; import styles from '../style.less'; type Props = { - domainData?: ISemantic.IDomainItem; + modelData?: ISemantic.IModelItem; dimensionList: ISemantic.IDimensionList; - domainId: number; + modelId: number; onSubmit: () => void; }; const FormItem = Form.Item; const EntityCreateForm: ForwardRefRenderFunction = ( - { domainData, dimensionList, domainId, onSubmit }, + { modelData, dimensionList, modelId, onSubmit }, ref, ) => { const [form] = Form.useForm(); @@ -27,15 +27,15 @@ const EntityCreateForm: ForwardRefRenderFunction = ( useEffect(() => { form.resetFields(); - if (!domainData?.entity) { + if (!modelData?.entity) { return; } - const { entity } = domainData; + const { entity } = modelData; form.setFieldsValue({ ...entity, name: entity.names.join(','), }); - }, [domainData]); + }, [modelData]); useImperativeHandle(ref, () => ({ getFormValidateFields, @@ -54,14 +54,14 @@ const EntityCreateForm: ForwardRefRenderFunction = ( const saveEntity = async () => { const values = await form.validateFields(); const { name } = values; - const { code, msg, data } = await updateDomain({ - ...domainData, + const { code, msg, data } = await updateModel({ + ...modelData, entity: { ...values, names: name.split(','), }, - id: domainId, - domainId, + id: modelId, + modelId, }); if (code === 200) { @@ -79,20 +79,11 @@ const EntityCreateForm: ForwardRefRenderFunction = ( - + - + + + + + + + + + + + + ); +}; + +export default ModelCreateFormModal; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/OverView.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/OverView.tsx index c7917e7b6..3c4f63e1f 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/OverView.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/OverView.tsx @@ -1,23 +1,36 @@ 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 { connect } from 'umi'; import icon from '../../../assets/icon/cloudEditor.svg'; import type { Dispatch } from 'umi'; import type { StateType } from '../model'; import { formatNumber } from '../../../utils/utils'; +import { deleteModel } from '../service'; +import ModelCreateFormModal from './ModelCreateFormModal'; import styles from './style.less'; type Props = { - modelList: ISemantic.IDomainItem[]; + disabledEdit?: boolean; + modelList: ISemantic.IModelItem[]; + onModelChange?: (model?: ISemantic.IModelItem) => void; domainManger: StateType; dispatch: Dispatch; }; -const OverView: React.FC = ({ domainManger, dispatch, modelList }) => { - const { selectDomainId } = domainManger; +const OverView: React.FC = ({ + modelList, + disabledEdit = false, + onModelChange, + domainManger, +}) => { + const { selectDomainId, selectModelId } = domainManger; + const [currentModel, setCurrentModel] = useState({}); + const [modelCreateFormModalVisible, setModelCreateFormModalVisible] = useState(false); - const extraNode = (model: ISemantic.IDomainItem) => { + const descNode = (model: ISemantic.IDomainItem) => { const { metricCnt, dimensionCnt } = model; return (
@@ -40,33 +53,103 @@ const OverView: React.FC = ({ domainManger, dispatch, modelList }) => {
); }; + + const extraNode = (model: ISemantic.IDomainItem) => { + return ( + { + domEvent.stopPropagation(); + if (key === 'edit') { + setCurrentModel(model); + setModelCreateFormModalVisible(true); + } + }, + items: [ + { + label: '编辑', + key: 'edit', + }, + { + label: ( + { + const { code, msg } = await deleteModel(model.id); + if (code === 200) { + onModelChange?.(); + } else { + message.error(msg); + } + }} + > +
删除 + + ), + key: 'delete', + }, + ], + }} + > + e.stopPropagation()} + /> + + ); + }; + return ( - <> - +
+ {!disabledEdit && ( +
+ +
+ )} + + {modelList && modelList.map((model: ISemantic.IDomainItem) => { return ( { - const { id, name } = model; - dispatch({ - type: 'domainManger/setSelectDomain', - selectDomainId: id, - selectDomainName: name, - domainData: model, - }); + onModelChange?.(model); }} /> ); })} - + {modelCreateFormModalVisible && ( + { + setModelCreateFormModalVisible(false); + onModelChange?.(); + }} + onCancel={() => { + setModelCreateFormModalVisible(false); + }} + /> + )} +
); }; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionAdminForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionAdminForm.tsx index 06afbc3c0..7b94d45ef 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionAdminForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionAdminForm.tsx @@ -6,10 +6,11 @@ import { connect } from 'umi'; import type { Dispatch } from 'umi'; import type { StateType } from '../../model'; import FormItemTitle from '@/components/FormHelper/FormItemTitle'; -import { updateDomain, getDomainDetail } from '../../service'; +import { updateDomain, updateModel, getDomainDetail, getModelDetail } from '../../service'; import styles from '../style.less'; type Props = { + permissionTarget: 'model' | 'domain'; dispatch: Dispatch; domainManger: StateType; onSubmit?: (data?: any) => void; @@ -18,15 +19,22 @@ type Props = { const FormItem = Form.Item; -const PermissionAdminForm: React.FC = ({ domainManger, onValuesChange }) => { +const PermissionAdminForm: React.FC = ({ + permissionTarget, + domainManger, + onValuesChange, +}) => { const [form] = Form.useForm(); const [isOpenState, setIsOpenState] = useState(true); const [classDetail, setClassDetail] = useState({}); - const { selectDomainId } = domainManger; + const { selectModelId: modelId, selectDomainId } = domainManger; const { APP_TARGET } = process.env; - const queryClassDetail = async (domainId: number) => { - const { code, msg, data } = await getDomainDetail({ domainId }); + const queryClassDetail = async () => { + const selectId = permissionTarget === 'model' ? modelId : selectDomainId; + const { code, msg, data } = await (permissionTarget === 'model' + ? getModelDetail + : getDomainDetail)({ modelId: selectId }); if (code === 200) { setClassDetail(data); const fieldsValue = { @@ -44,8 +52,8 @@ const PermissionAdminForm: React.FC = ({ domainManger, onValuesChange }) }; useEffect(() => { - queryClassDetail(selectDomainId); - }, [selectDomainId]); + queryClassDetail(); + }, [modelId]); const saveAuth = async () => { const values = await form.validateFields(); @@ -57,9 +65,10 @@ const PermissionAdminForm: React.FC = ({ domainManger, onValuesChange }) viewers, isOpen: isOpen ? 1 : 0, }; - const { code, msg } = await updateDomain(queryClassData); + const { code, msg } = await (permissionTarget === 'model' ? updateModel : updateDomain)( + queryClassData, + ); if (code === 200) { - // message.success('保存成功'); return; } message.error(msg); @@ -123,16 +132,6 @@ const PermissionAdminForm: React.FC = ({ domainManger, onValuesChange })
)} - {/* - - */} ); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionCreateDrawer.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionCreateDrawer.tsx index e7f4b29a2..a26af97c5 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionCreateDrawer.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionCreateDrawer.tsx @@ -14,7 +14,6 @@ import styles from '../style.less'; type Props = { domainManger: StateType; permissonData: any; - domainId: number; onCancel: () => void; visible: boolean; onSubmit: (params?: any) => void; @@ -25,11 +24,10 @@ const PermissionCreateDrawer: React.FC = ({ domainManger, visible, permissonData, - domainId, onCancel, onSubmit, }) => { - const { dimensionList, metricList } = domainManger; + const { dimensionList, metricList, selectModelId: modelId } = domainManger; const [form] = Form.useForm(); const basicInfoFormRef = useRef(null); const [selectedDimensionKeyList, setSelectedDimensionKeyList] = useState([]); @@ -65,7 +63,7 @@ const PermissionCreateDrawer: React.FC = ({ metrics: selectedMetricKeyList, }, ], - domainId, + modelId, }); if (code === 200) { @@ -136,11 +134,7 @@ const PermissionCreateDrawer: React.FC = ({
- + diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionCreateForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionCreateForm.tsx index 9da7c9fe0..d1048513b 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionCreateForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionCreateForm.tsx @@ -6,7 +6,6 @@ import SelectTMEPerson from '@/components/SelectTMEPerson'; import { formLayout } from '@/components/FormHelper/utils'; import styles from '../style.less'; type Props = { - domainId: number; permissonData: any; onSubmit?: (data?: any) => void; onValuesChange?: (value, values) => void; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionSection.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionSection.tsx index dd00c349d..692bdbf4a 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionSection.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionSection.tsx @@ -8,20 +8,20 @@ import PermissionTable from './PermissionTable'; import PermissionAdminForm from './PermissionAdminForm'; type Props = { + permissionTarget: 'model' | 'domain'; dispatch: Dispatch; domainManger: StateType; }; -const PermissionSection: React.FC = () => { +const PermissionSection: React.FC = ({ permissionTarget }) => { return ( <>
- + - - + {permissionTarget === 'model' && }
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionTable.tsx index 3859924bb..9d91b1e3e 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionTable.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Permission/PermissionTable.tsx @@ -19,7 +19,7 @@ type Props = { const PermissionTable: React.FC = ({ domainManger }) => { const { APP_TARGET } = process.env; const isInner = APP_TARGET === 'inner'; - const { dimensionList, metricList, selectDomainId } = domainManger; + const { dimensionList, metricList, selectModelId: modelId } = domainManger; const [createModalVisible, setCreateModalVisible] = useState(false); const [permissonData, setPermissonData] = useState({}); @@ -37,7 +37,7 @@ const PermissionTable: React.FC = ({ domainManger }) => { const actionRef = useRef(); const queryListData = async () => { - const { code, data } = await getGroupAuthInfo(selectDomainId); + const { code, data } = await getGroupAuthInfo(modelId); if (code === 200) { setIntentionList(data); return; @@ -46,10 +46,10 @@ const PermissionTable: React.FC = ({ domainManger }) => { }; useEffect(() => { - if (selectDomainId) { + if (modelId) { queryListData(); } - }, [selectDomainId]); + }, [modelId]); const queryDepartmentData = async () => { const { code, data } = await getOrganizationTree(); @@ -184,7 +184,7 @@ const PermissionTable: React.FC = ({ domainManger }) => { return ( { setPermissonData(record); setCreateModalVisible(true); @@ -216,7 +216,7 @@ const PermissionTable: React.FC = ({ domainManger }) => { cancelText="否" onConfirm={async () => { const { code, msg } = await removeGroupAuth({ - domainId: record.domainId, + modelId: record.modelId, groupId: record.groupId, }); if (code === 200) { @@ -228,7 +228,7 @@ const PermissionTable: React.FC = ({ domainManger }) => { }} > { setPermissonData(record); }} @@ -277,7 +277,6 @@ const PermissionTable: React.FC = ({ domainManger }) => { /> {createModalVisible && ( { diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ProjectInfoForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ProjectInfoForm.tsx index 0c2a06d84..1381d588b 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ProjectInfoForm.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ProjectInfoForm.tsx @@ -25,7 +25,6 @@ const ProjectInfoForm: React.FC = (props) => { const handleConfirm = async () => { const fieldsValue = await form.validateFields(); - // const columnsValue = { ...fieldsValue, isUnique: fieldsValue.isUnique === true ? 1 : 0 }; const columnsValue = { ...fieldsValue, isUnique: 1 }; setFormVals({ ...formVals, ...columnsValue }); setSaveLoading(true); @@ -97,24 +96,11 @@ const ProjectInfoForm: React.FC = (props) => { > - + diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts index a97c1f26e..31c4bc2e6 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts @@ -94,6 +94,30 @@ export declare namespace ISemantic { 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 { createdBy: string; updatedBy: string; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/model.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/model.ts index d8d58955b..8b380843f 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/model.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/model.ts @@ -6,14 +6,17 @@ import { getDimensionList, queryMetric, excuteSql, getDatabaseByDomainId } from export type StateType = { current: number; pageSize: number; + selectModelId: number; selectDomainId: number; selectDomainName: string; + selectModelName: string; dimensionList: ISemantic.IDimensionList; metricList: ISemantic.IMetricList; searchParams: Record; dataBaseResultColsMap: any; dataBaseConfig: any; domainData?: ISemantic.IDomainItem; + modelData?: ISemantic.IDomainItem; domainList: ISemantic.IDomainItem[]; }; @@ -28,6 +31,7 @@ export type ModelType = { }; reducers: { setSelectDomain: Reducer; + setSelectModel: Reducer; setDomainList: Reducer; setPagination: Reducer; setDimensionList: Reducer; @@ -42,7 +46,10 @@ export const defaultState: StateType = { current: 1, pageSize: 20, selectDomainId: 0, + selectModelId: 0, + modelData: undefined, selectDomainName: '', + selectModelName: '', searchParams: {}, dimensionList: [], metricList: [], @@ -123,6 +130,14 @@ const Model: ModelType = { domainData: action.domainData, }; }, + setSelectModel(state = defaultState, action) { + return { + ...state, + selectModelId: action.selectModelId, + selectModelName: action.selectModelName, + modelData: action.modelData, + }; + }, setDomainList(state = defaultState, action) { return { ...state, diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts index 70e109274..62c03d1b6 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts @@ -12,11 +12,11 @@ export function getDomainList(): Promise { } export function getDatasourceList(data: any): Promise { - 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 { - 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 { @@ -44,9 +44,15 @@ export function updateDatasource(data: any): Promise { } export function getDimensionList(data: any): Promise { - const { domainId } = data; + const { domainId, modelId } = data; 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') { return request.post(`${process.env.CHAT_API_BASE_URL}conf/dimension/page`, queryParams); @@ -67,9 +73,15 @@ export function updateDimension(data: any): Promise { } export function queryMetric(data: any): Promise { - const { domainId } = data; + const { domainId, modelId } = data; 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') { return request.post(`${process.env.CHAT_API_BASE_URL}conf/metric/page`, queryParams); @@ -89,8 +101,8 @@ export function updateExprMetric(data: any): Promise { }); } -export function getMeasureListByDomainId(domainId: number): Promise { - return request.get(`${process.env.API_BASE_URL}datasource/getMeasureListOfDomain/${domainId}`); +export function getMeasureListByModelId(modelId: number): Promise { + return request.get(`${process.env.API_BASE_URL}datasource/getMeasureListOfModel/${modelId}`); } export function deleteDatasource(id: any): Promise { @@ -117,12 +129,10 @@ export function deleteDomain(id: any): Promise { }); } -export function getGroupAuthInfo(id: number): Promise { +export function getGroupAuthInfo(modelId: number): Promise { return request(`${process.env.AUTH_API_BASE_URL}queryGroup`, { method: 'GET', - params: { - domainId: id, - }, + params: { modelId }, }); } @@ -169,7 +179,7 @@ export function getDomainExtendConfig(data: any): Promise { } export function getDomainExtendDetailConfig(data: any): Promise { - 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', }); } @@ -246,7 +256,7 @@ export function testDatabaseConnect(data: SaveDatabaseParams): Promise { type ExcuteSqlParams = { sql: string; - domainId: number; + modelId: number; }; // 执行脚本 @@ -272,3 +282,37 @@ export function getColumns(dbId: number, dbName: string, tableName: string): Pro method: 'GET', }); } + +export function getModelList(domainId: number): Promise { + 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 { + return request(`${process.env.API_BASE_URL}model/createModel`, { + method: 'POST', + data, + }); +} +export function updateModel(data: any): Promise { + return request(`${process.env.API_BASE_URL}model/updateModel`, { + method: 'POST', + data, + }); +} + +export function deleteModel(modelId: number): Promise { + return request(`${process.env.API_BASE_URL}model/deleteModel/${modelId}`, { + method: 'DELETE', + }); +} + +export function getModelDetail(data: any): Promise { + return request.get(`${process.env.API_BASE_URL}model/getModel/${data.modelId}`); +} diff --git a/webapp/packages/supersonic-fe/src/services/API.d.ts b/webapp/packages/supersonic-fe/src/services/API.d.ts index 7838dcf45..838714192 100644 --- a/webapp/packages/supersonic-fe/src/services/API.d.ts +++ b/webapp/packages/supersonic-fe/src/services/API.d.ts @@ -175,12 +175,12 @@ declare namespace API { comment: string; // 项目描述 creator: string; // 项目创建人 projectType: number; // 项目类别 0-为私有项目 1-为公共项目 - childProjectList?: ProjectList; - children?: ProjectList; + childDomainList?: DomainList; + children?: DomainList; value: string; }; - export type ProjectList = ProjectItem[]; + export type DomainList = ProjectItem[]; // 数据实例详情 export type DataInstanceDetail = {