[improvement][semantic-fe] Refactor database settings functionality.

This commit is contained in:
tristanliu
2023-09-04 12:29:07 +08:00
parent d5c5c63a75
commit f5a7068d5e
34 changed files with 1222 additions and 441 deletions

View File

@@ -40,14 +40,41 @@ const ROUTES = [
component: './Agent',
envEnableList: [ENV_KEY.CHAT],
},
{
path: '/semanticModel/model/:domainId?/:modelId?/:menuKey?',
component: './SemanticModel/DomainManager',
{
path: '/model',
name: 'semanticModel',
component: './SemanticModel/DomainManager',
envEnableList: [ENV_KEY.SEMANTIC],
routes: [
{
path: '/model',
redirect: '/model/:domainId?/:modelId?/:menuKey?',
},
{
path: '/model/:domainId?/:modelId?/:menuKey?',
component: './SemanticModel/DomainManager',
name: 'model',
envEnableList: [ENV_KEY.SEMANTIC],
},
{
path: '/database',
name: 'database',
component: './SemanticModel/components/Database/DatabaseTable',
envEnableList: [ENV_KEY.SEMANTIC],
},
],
},
{
path: '/Metric',
path: '/database',
name: 'database',
hideInMenu: true,
component: './SemanticModel/components/Database/DatabaseTable',
envEnableList: [ENV_KEY.SEMANTIC],
},
{
path: '/metric',
name: 'metric',
component: './SemanticModel/Metric',
envEnableList: [ENV_KEY.SEMANTIC],
@@ -61,10 +88,10 @@ const ROUTES = [
},
{
path: '/',
redirect: APP_TARGET === 'inner' ? '/semanticModel/model/' : '/chat',
redirect: APP_TARGET === 'inner' ? '/model' : '/chat',
envRedirect: {
[ENV_KEY.CHAT]: '/chat',
[ENV_KEY.SEMANTIC]: '/semanticModel/model',
[ENV_KEY.SEMANTIC]: '/model',
},
},
{

View File

@@ -141,7 +141,58 @@ ol {
backdrop-filter: blur(10px) !important;
}
.ant-menu.ant-menu-dark {
color: #fff;
}
.ant-menu-submenu-selected {
background-color: #296DF3 !important;
}
.ant-menu.ant-menu-dark .ant-menu-sub {
background-color: #fff;
.ant-menu-item-selected {
background-color: #296DF3;
.ant-pro-menu-item-title {
color: #fff;
}
}
.ant-menu-item {
&:hover {
background-color: #e3e3e3;
.ant-menu-item-selected {
background-color: #e3e3e3;
.ant-pro-menu-item-title {
color: #181a1a !important;
}
}
.ant-pro-menu-item-title {
color: #181a1a !important;
}
}
& > span > a {
color: #181a1a;
&:hover {
color: #181a1a;
}
}
}
}
.ant-menu-item:active {
background: inherit
}
// .ant-menu-dark .ant-menu-item > span > a {
// color: #181a1a;
// &:hover {
// color: #fff;
// }
// }
// .ant-menu-dark.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item-selected {
// // color: #fff;
// background-color: #1b4aef;
// }
.customizeHeader {
background-color: rgba(0, 0, 0, 0.2);

View File

@@ -11,7 +11,7 @@ type Props = {
const ChatSetting: React.FC<Props> = () => {
return (
<>
<Helmet title={'模型管理-超音数'} />
<Helmet title={'语义模型-超音数'} />
<OverviewContainer mode={'chatSetting'} />
</>
);

View File

@@ -0,0 +1,57 @@
// import { Tabs } from 'antd';
import React from 'react';
import { connect } from 'umi';
// import styles from '../components/style.less';
import type { StateType } from '../model';
import ProCard from '@ant-design/pro-card';
import EntitySection from '../components/Entity/EntitySection';
// import RecommendedQuestionsSection from '../components/Entity/RecommendedQuestionsSection';
import { ChatConfigType } from '../enum';
import type { Dispatch } from 'umi';
type Props = {
domainManger: StateType;
dispatch: Dispatch;
};
const ChatSettingSection: React.FC<Props> = () => {
// 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 style={{ width: 900, margin: '0 auto' }}>
{/* <Tabs
className={styles.chatSettingSectionTab}
items={isModelItem}
destroyInactiveTabPane
tabPosition="left"
/> */}
<ProCard bordered title="指标模式" style={{ marginBottom: 20 }}>
<EntitySection chatConfigType={ChatConfigType.AGG} />
</ProCard>
<ProCard bordered title="实体模式" style={{ marginBottom: 20 }}>
<EntitySection chatConfigType={ChatConfigType.DETAIL} />
</ProCard>
</div>
);
};
export default connect(({ domainManger }: { domainManger: StateType }) => ({
domainManger,
}))(ChatSettingSection);

View File

@@ -2,30 +2,27 @@ import React, { useEffect, useState } from 'react';
import { Form, Input, Spin, Select, message } from 'antd';
import type { FormInstance } from 'antd/lib/form';
import { getDbNames, getTables } from '../../service';
import { ISemantic } from '../../data';
const FormItem = Form.Item;
const { TextArea } = Input;
type Props = {
isEdit?: boolean;
dataBaseConfig: any;
databaseConfigList: ISemantic.IDatabaseItemList;
form: FormInstance<any>;
tableLoading?: boolean;
mode?: 'normal' | 'fast';
};
const DataSourceBasicForm: React.FC<Props> = ({
isEdit,
dataBaseConfig,
tableLoading = false,
mode = 'normal',
}) => {
const DataSourceBasicForm: React.FC<Props> = ({ isEdit, databaseConfigList, mode = 'normal' }) => {
const [currentDbLinkConfigId, setCurrentDbLinkConfigId] = useState<number>();
const [dbNameList, setDbNameList] = useState<any[]>([]);
const [tableNameList, setTableNameList] = useState<any[]>([]);
const [currentDbName, setCurrentDbName] = useState<string>('');
const [currentTableName, setCurrentTableName] = useState<string>('');
const [loading, setLoading] = useState<boolean>(false);
const queryDbNameList = async (databaseId: number) => {
setLoading(true);
const { code, data, msg } = await getDbNames(databaseId);
setLoading(false);
if (code === 200) {
const list = data?.resultList || [];
setDbNameList(list);
@@ -34,7 +31,12 @@ const DataSourceBasicForm: React.FC<Props> = ({
}
};
const queryTableNameList = async (databaseName: string) => {
const { code, data, msg } = await getTables(dataBaseConfig.id, databaseName);
if (!currentDbLinkConfigId) {
return;
}
setLoading(true);
const { code, data, msg } = await getTables(currentDbLinkConfigId, databaseName);
setLoading(false);
if (code === 200) {
const list = data?.resultList || [];
setTableNameList(list);
@@ -42,16 +44,32 @@ const DataSourceBasicForm: React.FC<Props> = ({
message.error(msg);
}
};
useEffect(() => {
if (dataBaseConfig?.id) {
queryDbNameList(dataBaseConfig.id);
}
}, [dataBaseConfig]);
return (
<Spin spinning={tableLoading}>
<Spin spinning={loading}>
{mode === 'fast' && (
<>
<FormItem
name="databaseId"
label="数据库连接"
rules={[{ required: true, message: '请选择数据库连接' }]}
>
<Select
showSearch
placeholder="请选择数据库连接"
disabled={isEdit}
onChange={(dbLinkConfigId: number) => {
queryDbNameList(dbLinkConfigId);
setCurrentDbLinkConfigId(dbLinkConfigId);
}}
>
{databaseConfigList.map((item) => (
<Select.Option key={item.id} value={item.id} disabled={!item.hasUsePermission}>
{item.name}
</Select.Option>
))}
</Select>
</FormItem>
<FormItem
name="dbName"
label="数据库名"
@@ -59,11 +77,10 @@ const DataSourceBasicForm: React.FC<Props> = ({
>
<Select
showSearch
placeholder="请选择数据库/表"
placeholder="请选择一个数据库连接"
disabled={isEdit}
onChange={(dbName: string) => {
queryTableNameList(dbName);
setCurrentDbName(dbName);
}}
>
{dbNameList.map((item) => (
@@ -78,15 +95,7 @@ const DataSourceBasicForm: React.FC<Props> = ({
label="数据表名"
rules={[{ required: true, message: '请选择数据库/表' }]}
>
<Select
placeholder="请选择数据库/表"
disabled={isEdit}
showSearch
onChange={(tableName: string) => {
// queryTableNameList(tableName);
setCurrentTableName(tableName);
}}
>
<Select placeholder="请选择数据库/表" disabled={isEdit} showSearch>
{tableNameList.map((item) => (
<Select.Option key={item.name} value={item.name}>
{item.name}

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from 'react';
import { Form, Button, Modal, Steps, message } from 'antd';
import BasicInfoForm from './DataSourceBasicForm';
import DataSourceBasicForm from './DataSourceBasicForm';
import FieldForm from './DataSourceFieldForm';
import { formLayout } from '@/components/FormHelper/utils';
import { EnumDataSourceType } from '../constants';
@@ -46,9 +46,10 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
const [currentStep, setCurrentStep] = useState(0);
const [saveLoading, setSaveLoading] = useState(false);
const [hasEmptyNameField, setHasEmptyNameField] = useState<boolean>(false);
const [formDatabaseId, setFormDatabaseId] = useState<number>();
const formValRef = useRef(initFormVal as any);
const [form] = Form.useForm();
const { dataBaseConfig, selectModelId: modelId } = domainManger;
const { databaseConfigList, selectModelId: modelId } = domainManger;
const updateFormVal = (val: any) => {
formValRef.current = val;
};
@@ -82,11 +83,13 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
bizName,
timeGranularity,
agg,
isCreateDimension,
isCreateDimension: createDimension,
name,
isCreateMetric,
isCreateMetric: createMetric,
dateFormat,
} = item;
const isCreateDimension = createDimension ? 1 : 0;
const isCreateMetric = createMetric ? 1 : 0;
switch (type) {
case EnumDataSourceType.CATEGORICAL:
fieldsClassify.dimensions.push({
@@ -157,7 +160,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
const queryParams = {
...submitForm,
sqlQuery: sql,
databaseId: dataSourceItem?.databaseId || dataBaseConfig.id,
databaseId: dataSourceItem?.databaseId || formDatabaseId,
queryType: basicInfoFormMode === 'fast' ? 'table_query' : 'sql_query',
tableQuery: dbName && tableName ? `${dbName}.${tableName}` : '',
modelId,
@@ -217,7 +220,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
if (queryType === 'table_query') {
const tableQueryString = tableQuery || '';
const [dbName, tableName] = tableQueryString.split('.');
columns = await queryTableColumnList(dbName, tableName);
columns = await queryTableColumnList(dataSourceItem.databaseId, dbName, tableName);
tableQueryInitValue = {
dbName,
tableName,
@@ -227,13 +230,14 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
};
const formatterInitData = (columns: any[], extendParams: Record<string, any> = {}) => {
const { id, name, bizName, description, datasourceDetail } = dataSourceItem as any;
const { id, name, bizName, description, datasourceDetail, databaseId } = dataSourceItem as any;
const { dimensions, identifiers, measures } = datasourceDetail;
const initValue = {
id,
name,
bizName,
description,
databaseId,
...extendParams,
// ...tableQueryInitValue,
};
@@ -274,11 +278,8 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
setFields(result);
};
const queryTableColumnList = async (dbName: string, tableName: string) => {
if (!dataBaseConfig?.id) {
return;
}
const { code, data, msg } = await getColumns(dataBaseConfig.id, dbName, tableName);
const queryTableColumnList = async (databaseId: number, dbName: string, tableName: string) => {
const { code, data, msg } = await getColumns(databaseId, dbName, tableName);
if (code === 200) {
const list = data?.resultList || [];
// setTableNameList(list);
@@ -299,16 +300,20 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
};
const renderContent = () => {
if (currentStep === 1) {
return <FieldForm fields={fields} onFieldChange={handleFieldChange} />;
}
return (
<BasicInfoForm
form={form}
isEdit={isEdit}
mode={basicInfoFormMode}
dataBaseConfig={dataBaseConfig}
/>
<>
<div style={{ display: currentStep === 1 ? 'block' : 'none' }}>
<FieldForm fields={fields} onFieldChange={handleFieldChange} />;
</div>
<div style={{ display: currentStep !== 1 ? 'block' : 'none' }}>
<DataSourceBasicForm
form={form}
isEdit={isEdit}
mode={basicInfoFormMode}
databaseConfigList={databaseConfigList}
/>
</div>
</>
);
};
@@ -365,9 +370,10 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
}}
onValuesChange={(value, values) => {
const { tableName } = value;
const { dbName } = values;
const { dbName, databaseId } = values;
setFormDatabaseId(databaseId);
if (tableName) {
queryTableColumnList(dbName, tableName);
queryTableColumnList(databaseId, dbName, tableName);
}
}}
className={styles.form}

View File

@@ -230,9 +230,12 @@ const FieldForm: React.FC<Props> = ({ fields, onFieldChange }) => {
style={{ marginBottom: '10px' }}
banner
message={
<Marquee pauseOnHover gradient={false}>
//
</Marquee>
<div>
//
</div>
// <Marquee pauseOnHover gradient={false}>
// 为了保障同一个主题域下维度/指标列表唯一,消除歧义,若本主题域下的多个数据源存在相同的字段名并且都勾选了快速创建,系统默认这些相同字段的指标维度是同一个,同时列表中将只显示最后一次创建的指标/维度。
// </Marquee>
}
/>
<Table<FieldItem>

View File

@@ -27,11 +27,12 @@ import 'ace-builds/src-min-noconflict/ext-searchbox';
import 'ace-builds/src-min-noconflict/theme-sqlserver';
import 'ace-builds/src-min-noconflict/theme-monokai';
import 'ace-builds/src-min-noconflict/mode-sql';
import { IDataSource, ISemantic } from '../../data';
type IProps = {
domainManger: StateType;
dispatch: Dispatch;
dataSourceItem: DataInstanceItem;
dataSourceItem: IDataSource.IDataSourceItem;
onUpdateSql?: (sql: string) => void;
sql?: string;
onSubmitSuccess?: (dataSourceInfo: any) => void;
@@ -61,7 +62,7 @@ const SqlDetail: React.FC<IProps> = ({
onUpdateSql,
onJdbcSourceChange,
}) => {
const { dataBaseConfig, selectModelId: modelId } = domainManger;
const { databaseConfigList, selectModelId: modelId } = domainManger;
const [resultTable, setResultTable] = useState<ResultTableItem[]>([]);
const [resultTableLoading, setResultTableLoading] = useState(false);
const [resultCols, setResultCols] = useState<ResultColItem[]>([]);
@@ -71,6 +72,7 @@ const SqlDetail: React.FC<IProps> = ({
total: 0,
});
const [jdbcSourceItems, setJdbcSourceItems] = useState<JdbcSourceItems[]>([]);
const [currentJdbcSourceItem, setCurrentJdbcSourceItem] = useState<JdbcSourceItems>();
const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false);
const [tableScroll, setTableScroll] = useState({
@@ -99,15 +101,40 @@ const SqlDetail: React.FC<IProps> = ({
const [scriptColumns, setScriptColumns] = useState<any[]>([]);
// useEffect(() => {
// const list = databaseConfigList.map((item: ISemantic.IDatabaseItem) => {
// return {
// label: item.name,
// key: item.id,
// disabled: !item.hasUsePermission,
// };
// });
// setJdbcSourceItems(list);
// const config = list[0];
// setCurrentJdbcSourceItem(config);
// onJdbcSourceChange?.(config?.key && Number(config?.key));
// }, [databaseConfigList]);
useEffect(() => {
setJdbcSourceItems([
{
label: dataBaseConfig?.name,
key: dataBaseConfig?.id,
},
]);
onJdbcSourceChange?.(dataBaseConfig?.id && Number(dataBaseConfig?.id));
}, [dataBaseConfig]);
const list = databaseConfigList.map((item: ISemantic.IDatabaseItem) => {
return {
label: item.name,
key: item.id,
disabled: !item.hasUsePermission,
};
});
setJdbcSourceItems(list);
let targetDataBase = list[0];
if (dataSourceItem?.id) {
const { databaseId } = dataSourceItem;
const target = list.find((item) => item.key === databaseId);
if (target) {
targetDataBase = target;
}
}
setCurrentJdbcSourceItem(targetDataBase);
// onJdbcSourceChange?.(targetDataBase?.key && Number(targetDataBase?.key));
}, [dataSourceItem, databaseConfigList]);
function creatCalcItem(key: string, data: string) {
const line = document.createElement('div'); // 需要每条数据一行,这样避免数据换行的时候获得的宽度不准确
@@ -209,10 +236,14 @@ const SqlDetail: React.FC<IProps> = ({
};
const separateSql = async (value: string) => {
if (!currentJdbcSourceItem?.key) {
return;
}
setResultTableLoading(true);
const { code, data, msg } = await excuteSql({
sql: value,
modelId,
// modelId,
id: currentJdbcSourceItem.key,
});
setResultTableLoading(false);
if (code === 200) {
@@ -378,6 +409,7 @@ const SqlDetail: React.FC<IProps> = ({
})[0];
if (target) {
// setJdbcSourceName(target.label);
setCurrentJdbcSourceItem(target);
onJdbcSourceChange?.(Number(value));
}
},
@@ -387,7 +419,7 @@ const SqlDetail: React.FC<IProps> = ({
<Button style={{ marginRight: '15px', minWidth: '140px' }}>
<Space>
<CloudServerOutlined className={styles.sqlOprIcon} style={{ marginRight: 0 }} />
<span>{jdbcSourceItems[0]?.label}</span>
<span>{currentJdbcSourceItem?.label}</span>
</Space>
</Button>
</Dropdown>

View File

@@ -29,10 +29,10 @@ export const TYPE_OPTIONS = [
label: '主键',
value: EnumDataSourceType.PRIMARY,
},
{
label: '外键',
value: EnumDataSourceType.FOREIGN,
},
// {
// label: '外键',
// value: EnumDataSourceType.FOREIGN,
// },
];
export const AGG_OPTIONS = [

View File

@@ -1,11 +1,3 @@
// 数据类型
export type DataInstanceItem = {
sourceInstanceId: number; // 数据实例id
sourceInstanceName: string; // 数据实例名
defaultSourceId: number; // 查询表需要的默认datasource id
bindSourceId: number;
};
// 任务查询结果列
export type TaskResultColumn = {
name: string;

View File

@@ -11,7 +11,7 @@ type Props = {
const DomainManager: React.FC<Props> = () => {
return (
<>
<Helmet title={'模型管理-超音数'} />
<Helmet title={'语义模型-超音数'} />
<OverviewContainer mode={'domain'} />
</>
);

View File

@@ -1,4 +1,4 @@
import { Form, Input } from 'antd';
import { Form, Input, Space, Row, Col } from 'antd';
import StandardFormRow from '@/components/StandardFormRow';
import TagSelect from '@/components/TagSelect';
import React, { useEffect } from 'react';
@@ -32,17 +32,17 @@ const MetricFilter: React.FC<Props> = ({ filterValues = {}, onFiltersChange }) =
};
const filterList = [
{
title: '指标类型',
key: 'type',
options: [
{
value: 'ATOMIC',
label: '原子指标',
},
{ value: 'DERIVED', label: '衍生指标' },
],
},
// {
// title: '指标类型',
// key: 'type',
// options: [
// {
// value: 'ATOMIC',
// label: '原子指标',
// },
// { value: 'DERIVED', label: '衍生指标' },
// ],
// },
{
title: '敏感度',
key: 'sensitiveLevel',
@@ -56,7 +56,7 @@ const MetricFilter: React.FC<Props> = ({ filterValues = {}, onFiltersChange }) =
form={form}
colon={false}
onValuesChange={(value, values) => {
if (value.name) {
if (value.key) {
return;
}
handleValuesChange(value, values);
@@ -64,38 +64,60 @@ const MetricFilter: React.FC<Props> = ({ filterValues = {}, onFiltersChange }) =
>
<StandardFormRow key="search" block>
<div className={styles.searchBox}>
<FormItem name={'key'} noStyle>
<div className={styles.searchInput}>
<Input.Search
placeholder="请输入需要查询指标的ID、指标名称、字段名称"
enterButton={<SearchOutlined style={{ marginTop: 5 }} />}
onSearch={onSearch}
/>
</div>
</FormItem>
<Row>
<Col flex="100px">
<span
style={{
fontSize: '18px',
fontWeight: 'bold',
position: 'relative',
top: '12px',
}}
>
</span>
</Col>
<Col flex="auto">
<FormItem name="key" noStyle>
<div className={styles.searchInput}>
<Input.Search
placeholder="请输入需要查询指标的ID、指标名称、字段名称"
enterButton={<SearchOutlined style={{ marginTop: 5 }} />}
onSearch={(value) => {
onSearch(value);
}}
/>
</div>
</FormItem>
</Col>
</Row>
</div>
</StandardFormRow>
{filterList.map((item) => {
const { title, key, options } = item;
return (
<StandardFormRow key={key} title={title} block>
<FormItem name={key}>
<TagSelect reverseCheckAll single>
{options.map((item: any) => (
<TagSelect.Option key={item.value} value={item.value}>
{item.label}
</TagSelect.Option>
))}
</TagSelect>
</FormItem>
</StandardFormRow>
);
})}
<StandardFormRow key="domainIds" title="所属主题域" block>
<FormItem name="domainIds">
<DomainTreeSelect />
</FormItem>
</StandardFormRow>
<Space size={80}>
<StandardFormRow key="domainIds" title="所属主题域" block>
<FormItem name="domainIds">
<DomainTreeSelect />
</FormItem>
</StandardFormRow>
{filterList.map((item) => {
const { title, key, options } = item;
return (
<StandardFormRow key={key} title={title} block>
<div style={{ marginLeft: -30 }}>
<FormItem name={key}>
<TagSelect reverseCheckAll single>
{options.map((item: any) => (
<TagSelect.Option key={item.value} value={item.value}>
{item.label}
</TagSelect.Option>
))}
</TagSelect>
</FormItem>
</div>
</StandardFormRow>
);
})}
</Space>
</Form>
);
};

View File

@@ -28,7 +28,7 @@ type QueryMetricListParams = {
};
const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
const { selectDomainId } = domainManger;
const { selectDomainId, selectModelId: modelId } = domainManger;
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const [pagination, setPagination] = useState({
current: 1,
@@ -45,13 +45,17 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
queryMetricList();
}, []);
const queryMetricList = async (params: QueryMetricListParams = {}) => {
setLoading(true);
const queryMetricList = async (params: QueryMetricListParams = {}, disabledLoading = false) => {
if (!disabledLoading) {
setLoading(true);
}
const { code, data, msg } = await queryMetric({
...pagination,
...params,
});
setLoading(false);
if (!disabledLoading) {
setLoading(false);
}
const { list, pageSize, current, total } = data || {};
let resData: any = {};
if (code === 200) {
@@ -86,15 +90,15 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
dataIndex: 'name',
title: '指标名称',
},
{
dataIndex: 'alias',
title: '别名',
search: false,
},
{
dataIndex: 'bizName',
title: '字段名称',
},
// {
// dataIndex: 'alias',
// title: '别名',
// search: false,
// },
// {
// dataIndex: 'bizName',
// title: '字段名称',
// },
{
dataIndex: 'modelName',
title: '所属模型',
@@ -114,14 +118,14 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
title: '描述',
search: false,
},
{
dataIndex: 'type',
title: '指标类型',
valueEnum: {
ATOMIC: '原子指标',
DERIVED: '衍生指标',
},
},
// {
// dataIndex: 'type',
// title: '指标类型',
// valueEnum: {
// ATOMIC: '原子指标',
// DERIVED: '衍生指标',
// },
// },
{
dataIndex: 'updatedAt',
@@ -178,7 +182,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
];
const handleFilterChange = async (filterParams: {
name: string;
key: string;
sensitiveLevel: string;
type: string;
}) => {
@@ -190,7 +194,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
params.sensitiveLevel = sensitiveLevelValue;
params.type = typeValue;
setFilterParams(params);
await queryMetricList(params);
await queryMetricList(params, filterParams.key ? false : true);
};
return (
@@ -231,6 +235,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
<MetricInfoCreateForm
domainId={Number(selectDomainId)}
createModalVisible={createModalVisible}
modelId={modelId}
metricItem={metricItem}
onSubmit={() => {
setCreateModalVisible(false);

View File

@@ -1,11 +1,11 @@
import { Popover, message, Space } from 'antd';
import React, { useEffect, useState } from 'react';
import { connect, Helmet, history, useParams, useRouteMatch, useLocation } from 'umi';
import { connect, Helmet, history, useParams } 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 { DownOutlined, LeftOutlined } from '@ant-design/icons';
import { ISemantic } from './data';
import { getDomainList, getModelList } from './service';
import ChatSettingTab from './ChatSetting/ChatSettingTab';
@@ -81,6 +81,23 @@ const OverviewContainer: React.FC<Props> = ({ mode, domainManger, dispatch }) =>
useEffect(() => {
initProjectTree();
dispatch({
type: 'domainManger/queryDatabaseList',
});
return () => {
dispatch({
type: 'domainManger/setSelectDomain',
selectDomainId: 0,
selectDomainName: '',
domainData: undefined,
});
dispatch({
type: 'domainManger/setSelectModel',
selectModelId: 0,
selectModelName: '',
modelData: undefined,
});
};
}, []);
useEffect(() => {
@@ -88,12 +105,6 @@ const OverviewContainer: React.FC<Props> = ({ mode, domainManger, dispatch }) =>
return;
}
queryModelList();
dispatch({
type: 'domainManger/queryDatabaseByDomainId',
payload: {
domainId: selectDomainId,
},
});
}, [selectDomainId]);
const queryModelList = async () => {
@@ -125,7 +136,6 @@ const OverviewContainer: React.FC<Props> = ({ mode, domainManger, dispatch }) =>
return;
}
setIsModel(false);
setActiveKey(menuKey);
}, [domainList, selectDomainId]);
const initModelConfig = () => {
@@ -155,8 +165,8 @@ const OverviewContainer: React.FC<Props> = ({ mode, domainManger, dispatch }) =>
}, [selectModelId]);
const pushUrlMenu = (domainId: number, modelId: number, menuKey: string) => {
const path = mode === 'domain' ? 'semanticModel' : 'chatSetting';
history.push(`/${path}/model/${domainId}/${modelId || 0}/${menuKey}`);
const path = mode === 'domain' ? 'model' : 'chatSetting/model';
history.push(`/${path}/${domainId}/${modelId || 0}/${menuKey}`);
};
const handleModelChange = (model?: ISemantic.IModelItem) => {
@@ -190,55 +200,70 @@ const OverviewContainer: React.FC<Props> = ({ mode, domainManger, dispatch }) =>
return (
<div className={styles.projectBody}>
<Helmet title={'模型管理-超音数'} />
<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>
{!!selectModelId && (
<div
className={styles.backBtn}
onClick={() => {
cleanModelInfo(selectDomainId);
}}
>
<LeftOutlined />
</div>
</Popover>
)}
<div className={styles.navContainer}>
<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={selectModelId ? false : 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>
{!selectModelId && (
<span className={styles.downIcon}>
<DownOutlined />
</span>
)}
</div>
</Popover>
</div>
</h2>
{selectDomainId ? (

View File

@@ -126,11 +126,11 @@ const NodeInfoDrawer: React.FC<Props> = ({
label: '敏感度',
value: SENSITIVE_LEVEL_ENUM[sensitiveLevel],
},
{
label: '指标类型',
value: MetricTypeWording[type],
hideItem: nodeType !== SemanticNodeType.METRIC,
},
// {
// label: '指标类型',
// value: MetricTypeWording[type],
// hideItem: nodeType !== SemanticNodeType.METRIC,
// },
],
},
{

View File

@@ -363,6 +363,30 @@ const DomainManger: React.FC<Props> = ({
setVisibleModeOpen(false);
}
const lessNodeZoomRealAndMoveCenter = () => {
const bbox = graphRef.current.get('group').getBBox();
// 计算图形的中心点
const centerX = (bbox.minX + bbox.maxX) / 2;
const centerY = (bbox.minY + bbox.maxY) / 2;
// 获取画布的中心点
const canvasWidth = graphRef.current.get('width');
const canvasHeight = graphRef.current.get('height');
const canvasCenterX = canvasWidth / 2;
const canvasCenterY = canvasHeight / 2;
// 计算画布需要移动的距离
const dx = canvasCenterX - centerX;
const dy = canvasCenterY - centerY;
// 将画布移动到中心点
graphRef.current.translate(dx, dy);
// 将缩放比例设置为 1以画布中心点为中心进行缩放
graphRef.current.zoomTo(1, { x: canvasCenterX, y: canvasCenterY });
};
useEffect(() => {
if (!Array.isArray(graphData?.children)) {
return;
@@ -474,9 +498,13 @@ const DomainManger: React.FC<Props> = ({
});
graphRef.current.data(graphData);
graphRef.current.render();
graphRef.current.fitView([80, 80]);
// setAllActiveLegend(legend);
const nodeCount = graphRef.current.getNodes().length;
if (nodeCount < 10) {
lessNodeZoomRealAndMoveCenter();
} else {
graphRef.current.fitView([80, 80]);
}
graphRef.current.on('node:click', (evt: any) => {
const item = evt.item; // 被操作的节点 item

View File

@@ -28,7 +28,7 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
onCancel,
dispatch,
}) => {
const { selectDomainId, dataBaseConfig, selectModelId } = domainManger;
const { selectDomainId } = domainManger;
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false);
@@ -77,66 +77,47 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
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);
<Row gutter={16} style={{ marginTop: '0px' }}>
<Col span={12}>
<Card
hoverable
style={{ height: 220 }}
onClick={() => {
onTypeChange?.('fast');
setCreateDataSourceModalOpen(false);
setDataSourceModalVisible(true);
}}
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);
setDataSourceModalVisible(true);
}}
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);
setCreateModalVisible(true);
}}
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}/0/dataBase`);
onCancel?.();
}}
>
</Button>
}
/>
)}
setCreateModalVisible(true);
}}
hoverable
style={{ height: 220 }}
cover={
<ConsoleSqlOutlined
style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
/>
}
>
<Meta title="SQL脚本" description="自定义SQL脚本创建数据源" />
</Card>
</Col>
</Row>
</Modal>
{dataSourceModalVisible && (

View File

@@ -83,6 +83,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
{
dataIndex: 'id',
title: 'ID',
width: 80,
order: 100,
},
{
@@ -92,16 +93,19 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
{
dataIndex: 'alias',
title: '别名',
width: 300,
ellipsis: true,
search: false,
},
{
dataIndex: 'bizName',
title: '字段名称',
order: 9,
// order: 9,
},
{
dataIndex: 'sensitiveLevel',
title: '敏感度',
width: 80,
valueEnum: SENSITIVE_LEVEL_ENUM,
},

View File

@@ -64,6 +64,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
{
dataIndex: 'id',
title: 'ID',
width: 80,
},
{
dataIndex: 'name',
@@ -72,6 +73,8 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
{
dataIndex: 'alias',
title: '别名',
width: 300,
ellipsis: true,
search: false,
},
{
@@ -81,6 +84,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
{
dataIndex: 'sensitiveLevel',
title: '敏感度',
width: 80,
valueEnum: SENSITIVE_LEVEL_ENUM,
},
{
@@ -93,24 +97,14 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
title: '描述',
search: false,
},
{
dataIndex: 'type',
title: '指标类型',
valueEnum: {
ATOMIC: '原子指标',
DERIVED: '衍生指标',
},
// render: (type: any) => {
// switch (type) {
// case 'ATOMIC':
// return '原子指标';
// case 'DERIVED':
// return '衍生指标';
// default:
// return '未知';
// }
// },
},
// {
// dataIndex: 'type',
// title: '指标类型',
// valueEnum: {
// ATOMIC: '原子指标',
// DERIVED: '衍生指标',
// },
// },
{
dataIndex: 'updatedAt',

View File

@@ -3,19 +3,22 @@ import type { ForwardRefRenderFunction } from 'react';
import { message, Form, Input, Select, Button, Space } from 'antd';
import { saveDatabase, testDatabaseConnect } from '../../service';
import { formLayout } from '@/components/FormHelper/utils';
import SelectTMEPerson from '@/components/SelectTMEPerson';
import { ISemantic } from '../../data';
import styles from '../style.less';
type Props = {
domainId: number;
dataBaseConfig: any;
onSubmit: (params?: any) => void;
domainId?: number;
dataBaseConfig?: ISemantic.IDatabaseItem;
hideSubmitBtn?: boolean;
onSubmit?: (params?: any) => void;
};
const FormItem = Form.Item;
const TextArea = Input.TextArea;
const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
{ domainId, dataBaseConfig, onSubmit },
{ domainId, dataBaseConfig, onSubmit, hideSubmitBtn = false },
ref,
) => {
const [form] = Form.useForm();
@@ -25,8 +28,10 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
useEffect(() => {
form.resetFields();
form.setFieldsValue({ ...dataBaseConfig });
setSelectedDbType(dataBaseConfig?.type);
if (dataBaseConfig) {
form.setFieldsValue({ ...dataBaseConfig });
setSelectedDbType(dataBaseConfig?.type);
}
}, [dataBaseConfig]);
const getFormValidateFields = async () => {
@@ -35,11 +40,14 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
useImperativeHandle(ref, () => ({
getFormValidateFields,
saveDatabaseConfig,
testDatabaseConnection,
}));
const saveDatabaseConfig = async () => {
const values = await form.validateFields();
const { code, msg } = await saveDatabase({
...dataBaseConfig,
...values,
domainId,
});
@@ -115,6 +123,23 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
</FormItem>
</>
)}
{selectedDbType === 'mysql' && (
<FormItem
name="version"
label="数据库版本"
rules={[{ required: true, message: '请选择数据库版本' }]}
>
<Select
style={{ width: '100%' }}
placeholder="请选择数据库版本"
options={[
{ value: '5.7', label: '5.7' },
{ value: '8.0', label: '8.0' },
]}
/>
</FormItem>
)}
<FormItem
name="username"
label="用户名"
@@ -128,34 +153,44 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
<FormItem name="database" label="数据库名称">
<Input placeholder="请输入数据库名称" />
</FormItem>
<FormItem name="version" label="数据库版本">
<Input placeholder="请输入数据库版本" />
<FormItem
name="admins"
label="管理员"
// rules={[{ required: true, message: '请设定数据库连接管理者' }]}
>
<SelectTMEPerson placeholder="请邀请团队成员" />
</FormItem>
<FormItem name="viewers" label="使用者">
<SelectTMEPerson placeholder="请邀请团队成员" />
</FormItem>
<FormItem name="description" label="描述">
<TextArea placeholder="请输入数据库描述" style={{ height: 100 }} />
</FormItem>
<FormItem>
<Space>
<Button
type="primary"
loading={testLoading}
onClick={() => {
testDatabaseConnection();
}}
>
</Button>
{!hideSubmitBtn && (
<FormItem>
<Space>
<Button
type="primary"
loading={testLoading}
onClick={() => {
testDatabaseConnection();
}}
>
</Button>
<Button
type="primary"
onClick={() => {
saveDatabaseConfig();
}}
>
</Button>
</Space>
</FormItem>
<Button
type="primary"
onClick={() => {
saveDatabaseConfig();
}}
>
</Button>
</Space>
</FormItem>
)}
</Form>
</>
);

View File

@@ -0,0 +1,79 @@
import React, { useState, useRef } from 'react';
import { Button, Modal, Space } from 'antd';
import DatabaseCreateForm from './DatabaseCreateForm';
import { ISemantic } from '../../data';
export type CreateFormProps = {
onCancel: () => void;
databaseItem?: ISemantic.IDatabaseItem;
open: boolean;
onSubmit: (values?: any) => void;
};
const DatabaseSettingModal: React.FC<CreateFormProps> = ({
onCancel,
databaseItem,
open,
onSubmit,
}) => {
const [testLoading, setTestLoading] = useState<boolean>(false);
const createFormRef = useRef<any>({});
const handleTestConnection = async () => {
setTestLoading(true);
await createFormRef.current.testDatabaseConnection();
setTestLoading(false);
};
const renderFooter = () => {
return (
<>
<Space>
<Button
type="primary"
loading={testLoading}
onClick={() => {
handleTestConnection();
}}
>
</Button>
<Button
type="primary"
onClick={() => {
createFormRef.current.saveDatabaseConfig();
}}
>
</Button>
</Space>
</>
);
};
return (
<Modal
width={1200}
destroyOnClose
title="数据库连接设置"
style={{ top: 48 }}
maskClosable={false}
open={open}
footer={renderFooter()}
onCancel={onCancel}
>
<DatabaseCreateForm
hideSubmitBtn={true}
ref={createFormRef}
dataBaseConfig={databaseItem}
onSubmit={() => {
onSubmit?.();
}}
/>
</Modal>
);
};
export default DatabaseSettingModal;

View File

@@ -0,0 +1,162 @@
import type { ActionType, ProColumns } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
import { message, Button, Space, Popconfirm } from 'antd';
import React, { useRef, useState, useEffect } from 'react';
import DatabaseSettingModal from './DatabaseSettingModal';
import { ISemantic } from '../../data';
import { getDatabaseList, deleteDatabase } from '../../service';
import moment from 'moment';
import styles from '../style.less';
type Props = {};
const DatabaseTable: React.FC<Props> = ({}) => {
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const [databaseItem, setDatabaseItem] = useState<ISemantic.IDatabaseItem>();
const [dataBaseList, setDataBaseList] = useState<any[]>([]);
const actionRef = useRef<ActionType>();
const queryDatabaseList = async () => {
const { code, data, msg } = await getDatabaseList();
if (code === 200) {
setDataBaseList(data);
} else {
message.error(msg);
}
};
useEffect(() => {
queryDatabaseList();
}, []);
const columns: ProColumns[] = [
{
dataIndex: 'id',
title: 'ID',
width: 80,
},
{
dataIndex: 'name',
title: '连接名称',
},
{
dataIndex: 'type',
title: '类型',
search: false,
},
{
dataIndex: 'createdBy',
title: '创建人',
search: false,
},
{
dataIndex: 'description',
title: '描述',
search: false,
},
{
dataIndex: 'updatedAt',
title: '更新时间',
search: false,
render: (value: any) => {
return value && value !== '-' ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-';
},
},
{
title: '操作',
dataIndex: 'x',
valueType: 'option',
render: (_, record) => {
if (!record.hasEditPermission) {
return <></>;
}
return (
<Space>
<a
key="dimensionEditBtn"
onClick={() => {
setDatabaseItem(record);
setCreateModalVisible(true);
}}
>
</a>
<Popconfirm
title="确认删除?"
okText="是"
cancelText="否"
onConfirm={async () => {
const { code, msg } = await deleteDatabase(record.id);
if (code === 200) {
setDatabaseItem(undefined);
queryDatabaseList();
} else {
message.error(msg);
}
}}
>
<a
key="dimensionDeleteEditBtn"
onClick={() => {
setDatabaseItem(record);
}}
>
</a>
</Popconfirm>
</Space>
);
},
},
];
return (
<div style={{ margin: 20 }}>
<ProTable
className={`${styles.classTable} ${styles.classTableSelectColumnAlignLeft}`}
actionRef={actionRef}
rowKey="id"
columns={columns}
dataSource={dataBaseList}
search={false}
tableAlertRender={() => {
return false;
}}
size="small"
options={{ reload: false, density: false, fullScreen: false }}
toolBarRender={() => [
<Button
key="create"
type="primary"
onClick={() => {
setDatabaseItem(undefined);
setCreateModalVisible(true);
}}
>
</Button>,
]}
/>
{createModalVisible && (
<DatabaseSettingModal
open={createModalVisible}
databaseItem={databaseItem}
onCancel={() => {
setCreateModalVisible(false);
}}
onSubmit={() => {
setCreateModalVisible(false);
queryDatabaseList();
}}
/>
)}
</div>
);
};
export default DatabaseTable;

View File

@@ -1,12 +1,13 @@
import React, { useEffect, useState } from 'react';
import { Button, Form, Input, Modal, Select } from 'antd';
import { Button, Form, Input, Modal, Select, Row, Col, Space, Tooltip } from 'antd';
import { SENSITIVE_LEVEL_OPTIONS } from '../constant';
import { formLayout } from '@/components/FormHelper/utils';
import SqlEditor from '@/components/SqlEditor';
import InfoTagList from './InfoTagList';
import { ISemantic } from '../data';
import { createDimension, updateDimension } from '../service';
// import DimensionValueSettingModal from './DimensionValueSettingModal';
import { InfoCircleOutlined } from '@ant-design/icons';
import { createDimension, updateDimension, mockDimensionAlias } from '../service';
import { message } from 'antd';
export type CreateFormProps = {
@@ -37,8 +38,8 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
>([]);
const [form] = Form.useForm();
const { setFieldsValue, resetFields } = form;
// const [dimensionValueSettingModalVisible, setDimensionValueSettingModalVisible] =
// useState<boolean>(false);
const [llmLoading, setLlmLoading] = useState<boolean>(false);
const handleSubmit = async (
isSilenceSubmit = false,
dimValueMaps?: ISemantic.IDimensionValueSettingItem[],
@@ -48,6 +49,7 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
{
...fieldsValue,
dimValueMaps: dimValueMaps || dimensionValueSettingList,
alias: Array.isArray(fieldsValue.alias) ? fieldsValue.alias.join(',') : '',
},
isSilenceSubmit,
);
@@ -75,7 +77,10 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
};
const setFormVal = () => {
setFieldsValue(dimensionItem);
if (dimensionItem) {
const { alias } = dimensionItem;
setFieldsValue({ ...dimensionItem, alias: alias && alias.trim() ? alias.split(',') : [] });
}
};
useEffect(() => {
@@ -110,6 +115,24 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
);
};
const generatorDimensionAlias = async () => {
const fieldsValue = await form.validateFields();
setLlmLoading(true);
const { code, data } = await mockDimensionAlias({
...dimensionItem,
...fieldsValue,
alias: fieldsValue.alias?.join(','),
});
setLlmLoading(false);
const formAlias = form.getFieldValue('alias');
setLlmLoading(false);
if (code === 200) {
form.setFieldValue('alias', Array.from(new Set([...formAlias, ...data])));
} else {
message.error('大语言模型解析异常');
}
};
const renderContent = () => {
return (
<>
@@ -131,7 +154,6 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
>
<Input placeholder="名称不可重复" disabled={isEdit} />
</FormItem>
<FormItem
hidden={isEdit}
name="datasourceId"
@@ -146,8 +168,39 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
))}
</Select>
</FormItem>
<FormItem name="alias" label="别名">
<Input placeholder="多个别名用英文逗号隔开" />
<FormItem label="别名">
<Row>
<Col flex="1 1 200px">
<FormItem name="alias" noStyle>
<Select
mode="tags"
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
tokenSeparators={[',']}
maxTagCount={9}
/>
</FormItem>
</Col>
{isEdit && (
<Col flex="0 1 75px">
<Button
type="link"
size="small"
loading={llmLoading}
style={{ top: '2px' }}
onClick={() => {
generatorDimensionAlias();
}}
>
<Space>
<Tooltip title="智能填充将根据维度相关信息,使用大语言模型获取维度别名">
<InfoCircleOutlined />
</Tooltip>
</Space>
</Button>
</Col>
)}
</Row>
</FormItem>
<FormItem
name="semanticType"
@@ -185,16 +238,6 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
>
<TextArea placeholder="请输入维度描述" />
</FormItem>
{/* <FormItem name="dimValueMaps" label="维度值设置">
<Button
type="primary"
onClick={() => {
setDimensionValueSettingModalVisible(true);
}}
>
设置
</Button>
</FormItem> */}
<FormItem
name="expr"
label="表达式"
@@ -223,22 +266,6 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
{renderContent()}
</Form>
</Modal>
{/* {dimensionValueSettingModalVisible && (
<DimensionValueSettingModal
dimensionValueSettingList={dimensionValueSettingList}
open={dimensionValueSettingModalVisible}
onCancel={() => {
setDimensionValueSettingModalVisible(false);
}}
onSubmit={(dimValueMaps) => {
if (isEdit) {
handleSubmit(true, dimValueMaps);
}
setDimensionValueSettingList(dimValueMaps);
setDimensionValueSettingModalVisible(false);
}}
/>
)} */}
</>
);
};

View File

@@ -1,8 +1,9 @@
import React, { useEffect, useState } from 'react';
import { Button, Modal, message } from 'antd';
import { Button, Modal, message, Space, Tooltip } from 'antd';
import { InfoCircleOutlined } from '@ant-design/icons';
import { ISemantic } from '../data';
import CommonEditTable from './CommonEditTable';
import { createDimension, updateDimension } from '../service';
import { updateDimension, mockDimensionValuesAlias } from '../service';
import { connect } from 'umi';
import type { StateType } from '../model';
@@ -15,7 +16,7 @@ export type CreateFormProps = {
domainManger: StateType;
};
type TableDataSource = { techName: string; bizName: string; alias: string };
type TableDataSource = { techName: string; bizName: string; alias?: string[] };
const DimensionInfoModal: React.FC<CreateFormProps> = ({
onCancel,
@@ -28,16 +29,10 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
const [tableDataSource, setTableDataSource] = useState<TableDataSource[]>([]);
const { selectDomainId } = domainManger;
const [dimValueMaps, setDimValueMaps] = useState<ISemantic.IDimensionValueSettingItem[]>([]);
const [llmLoading, setLlmLoading] = useState<boolean>(false);
useEffect(() => {
const dataSource = dimensionValueSettingList.map((item) => {
const { alias } = item;
return {
...item,
alias: Array.isArray(alias) ? alias.join(',') : '',
};
});
setTableDataSource(dataSource);
setTableDataSource(dimensionValueSettingList);
setDimValueMaps(dimensionValueSettingList);
}, [dimensionValueSettingList]);
@@ -62,9 +57,37 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
message.error(msg);
};
const generatorDimensionValue = async () => {
setLlmLoading(true);
const { code, data } = await mockDimensionValuesAlias({ ...dimensionItem });
setLlmLoading(false);
if (code === 200) {
if (Array.isArray(data)) {
setDimValueMaps([...dimValueMaps, ...data]);
setTableDataSource([...tableDataSource, ...data]);
}
} else {
message.error('大语言模型解析异常');
}
};
const renderFooter = () => {
return (
<>
<Button
type="primary"
loading={llmLoading}
onClick={() => {
generatorDimensionValue();
}}
>
<Space>
<Tooltip title="智能填充将根据维度相关信息,使用大语言模型获取可能被使用的维度值">
<InfoCircleOutlined />
</Tooltip>
</Space>
</Button>
<Button onClick={onCancel}></Button>
<Button
type="primary"
@@ -83,6 +106,7 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
title: '技术名称',
dataIndex: 'techName',
width: 200,
tooltip: '数据库中存储的维度值数据。 比如数据库中维度平台的维度值有kw、qy等',
formItemProps: {
fieldProps: {
placeholder: '请填写技术名称',
@@ -100,6 +124,8 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
title: '业务名称',
dataIndex: 'bizName',
width: 200,
tooltip:
'查询完成后,最终返回给用户的维度值信息。比如将技术名称kw转换成酷我平台,最终返回给用户是酷我平台',
fieldProps: {
placeholder: '请填写业务名称',
},
@@ -116,15 +142,21 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
{
title: '别名',
dataIndex: 'alias',
valueType: 'select',
width: 500,
tooltip:
'解析用户查询意图时,支持别名到技术名称的转换。比如用户输入kw、kuwo、酷我,完成设置后,都可以将其转换成技术名称kw',
fieldProps: {
placeholder: '多个别名用英文逗号隔开',
placeholder: '输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔',
mode: 'tags',
maxTagCount: 5,
tokenSeparators: [','],
},
},
];
return (
<Modal
width={1000}
width={1200}
destroyOnClose
title="维度值设置"
style={{ top: 48 }}
@@ -140,7 +172,7 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
const dimValueMaps = tableData.map((item: TableDataSource) => {
return {
...item,
alias: item.alias ? `${item.alias}`.split(',') : [],
// alias: item.alias ? `${item.alias}`.split(',') : [],
};
});

View File

@@ -6,14 +6,16 @@ import ClassDataSourceTable from './ClassDataSourceTable';
import ClassDimensionTable from './ClassDimensionTable';
import ClassMetricTable from './ClassMetricTable';
import PermissionSection from './Permission/PermissionSection';
import DatabaseSection from './Database/DatabaseSection';
// import DatabaseSection from './Database/DatabaseSection';
import EntitySettingSection from './Entity/EntitySettingSection';
import ChatSettingSection from '../ChatSetting/ChatSettingSection';
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 RecommendedQuestionsSection from '../components/Entity/RecommendedQuestionsSection';
import type { Dispatch } from 'umi';
@@ -50,11 +52,11 @@ const DomainManagerTab: React.FC<Props> = ({
/>
),
},
{
label: '数据库',
key: 'dataBase',
children: <DatabaseSection />,
},
// {
// label: '数据库',
// key: 'dataBase',
// children: <DatabaseSection />,
// },
{
label: '权限管理',
key: 'permissonSetting',
@@ -93,13 +95,27 @@ const DomainManagerTab: React.FC<Props> = ({
key: 'entity',
children: <EntitySettingSection />,
},
{
label: '权限管理',
key: 'permissonSetting',
children: <PermissionSection permissionTarget={'model'} />,
},
];
{
label: '问答设置',
key: 'chatSetting',
children: <ChatSettingSection />,
},
{
label: '推荐问题',
key: 'recommendedQuestions',
children: <RecommendedQuestionsSection />,
},
].filter((item) => {
if (window.RUNNING_ENV === 'semantic') {
return !['chatSetting', 'recommendedQuestions'].includes(item.key);
}
return item;
});
return (
<>

View File

@@ -12,6 +12,7 @@ import { ISemantic } from '../data';
type Props = {
value?: any;
width?: number | string;
onChange?: () => void;
treeSelectProps?: Record<string, any>;
domainList: ISemantic.IDomainItem[];
@@ -20,6 +21,7 @@ type Props = {
const DomainTreeSelect: FC<Props> = ({
value,
width = 300,
onChange,
treeSelectProps = {},
domainList,
@@ -51,7 +53,12 @@ const DomainTreeSelect: FC<Props> = ({
}, [domainList]);
return (
<div className={styles.domainTreeSelect}>
<div
className={styles.domainTreeSelect}
style={{
width,
}}
>
<TreeSelect
showSearch
style={{ width: '100%' }}

View File

@@ -53,7 +53,7 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
const saveEntity = async () => {
const values = await form.validateFields();
const { name } = values;
const { name = '' } = values;
const { code, msg, data } = await updateModel({
...modelData,
entity: {

View File

@@ -1,5 +1,5 @@
import { message, Space } from 'antd';
import React, { useState, useEffect, useRef } from 'react';
import React, { useState, useEffect } from 'react';
import type { Dispatch } from 'umi';
import { connect } from 'umi';
import type { StateType } from '../../model';

View File

@@ -6,18 +6,25 @@ import {
Steps,
Input,
Select,
Radio,
Switch,
InputNumber,
message,
Result,
Row,
Col,
Space,
Tooltip,
} from 'antd';
import { InfoCircleOutlined } from '@ant-design/icons';
import MetricMeasuresFormTable from './MetricMeasuresFormTable';
import { SENSITIVE_LEVEL_OPTIONS } from '../constant';
import { formLayout } from '@/components/FormHelper/utils';
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
import styles from './style.less';
import { getMeasureListByModelId } from '../service';
import { creatExprMetric, updateExprMetric } from '../service';
import TableTitleTooltips from '../components/TableTitleTooltips';
import { creatExprMetric, updateExprMetric, mockMetricAlias } from '../service';
import { ISemantic } from '../data';
import { history } from 'umi';
@@ -50,7 +57,11 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
const formValRef = useRef({} as any);
const [form] = Form.useForm();
const updateFormVal = (val: any) => {
formValRef.current = val;
const formVal = {
...formValRef.current,
...val,
};
formValRef.current = formVal;
};
const [classMeasureList, setClassMeasureList] = useState<ISemantic.IMeasure[]>([]);
@@ -60,7 +71,9 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
const [exprSql, setExprSql] = useState<string>('');
const [isPercentState, setIsPercentState] = useState<boolean>(false);
const [isDecimalState, setIsDecimalState] = useState<boolean>(false);
const [hasMeasuresState, setHasMeasuresState] = useState<boolean>(true);
const [llmLoading, setLlmLoading] = useState<boolean>(false);
const forward = () => setCurrentStep(currentStep + 1);
const backward = () => setCurrentStep(currentStep - 1);
@@ -93,7 +106,6 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
expr: exprSql,
measures: exprTypeParamsState,
},
dataFormatType: isPercentState ? 'percent' : '',
};
updateFormVal(submitForm);
if (currentStep < 1) {
@@ -116,14 +128,16 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
alias,
} = metricItem as any;
const isPercent = dataFormatType === 'percent';
const isDecimal = dataFormatType === 'decimal';
const initValue = {
id,
name,
bizName,
sensitiveLevel,
description,
isPercent,
alias,
// isPercent,
dataFormatType,
alias: alias && alias.trim() ? alias.split(',') : [],
dataFormat: dataFormat || {
decimalPlaces: 2,
needMultiply100: false,
@@ -138,6 +152,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
setExprTypeParamsState(typeParams.measures);
setExprSql(typeParams.expr);
setIsPercentState(isPercent);
setIsDecimalState(isDecimal);
};
useEffect(() => {
@@ -151,11 +166,15 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
modelId,
...fieldsValue,
};
const { typeParams } = queryParams;
const { typeParams, alias, dataFormatType } = queryParams;
queryParams.alias = Array.isArray(alias) ? alias.join(',') : '';
if (!typeParams?.expr) {
message.error('请输入度量表达式');
return;
}
if (!dataFormatType) {
delete queryParams.dataFormat;
}
if (!(Array.isArray(typeParams?.measures) && typeParams.measures.length > 0)) {
message.error('请添加一个度量');
return;
@@ -173,6 +192,18 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
message.error(msg);
};
const generatorMetricAlias = async () => {
setLlmLoading(true);
const { code, data } = await mockMetricAlias({ ...metricItem });
const formAlias = form.getFieldValue('alias');
setLlmLoading(false);
if (code === 200) {
form.setFieldValue('alias', Array.from(new Set([...formAlias, ...data])));
} else {
message.error('大语言模型解析异常');
}
};
const renderContent = () => {
if (currentStep === 1) {
return (
@@ -212,8 +243,39 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
>
<Input placeholder="名称不可重复" disabled={isEdit} />
</FormItem>
<FormItem name="alias" label="别名">
<Input placeholder="多个别名用英文逗号隔开" />
<FormItem label="别名">
<Row>
<Col flex="1 1 200px">
<FormItem name="alias" noStyle>
<Select
mode="tags"
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
tokenSeparators={[',']}
maxTagCount={9}
/>
</FormItem>
</Col>
{isEdit && (
<Col flex="0 1 75px">
<Button
type="link"
loading={llmLoading}
size="small"
style={{ top: '2px' }}
onClick={() => {
generatorMetricAlias();
}}
>
<Space>
<Tooltip title="智能填充将根据指标相关信息,使用大语言模型获取指标别名">
<InfoCircleOutlined />
</Tooltip>
</Space>
</Button>
</Col>
)}
</Row>
</FormItem>
<FormItem
name="sensitiveLevel"
@@ -230,12 +292,47 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
</FormItem>
<FormItem
name="description"
label="指标描述"
rules={[{ required: true, message: '请输入指标描述' }]}
label={
<TableTitleTooltips
title="业务口径"
overlayInnerStyle={{ width: 600 }}
tooltips={
<>
<p>
使使
</p>
<p>1. </p>
<p>2. </p>
<p>3. 使使</p>
<p>4. </p>
<p>
便
</p>
</>
}
/>
}
rules={[{ required: true, message: '请输入业务口径' }]}
>
<TextArea placeholder="请输入指标描述" />
<TextArea placeholder="请输入业务口径" />
</FormItem>
<FormItem
label={
<FormItemTitle
title={'数据格式化'}
// subTitle={'开启后指标数据展示时会根据配置进行格式化如0.02 -> 2%'}
/>
}
name="dataFormatType"
>
<Radio.Group buttonStyle="solid" size="middle">
<Radio.Button value=""></Radio.Button>
<Radio.Button value="decimal"></Radio.Button>
<Radio.Button value="percent"></Radio.Button>
</Radio.Group>
</FormItem>
{/* <FormItem
label={
<FormItemTitle
title={'是否展示为百分比'}
@@ -250,20 +347,24 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
form.setFieldValue(['dataFormat', 'needMultiply100'], checked);
}}
/>
</FormItem>
</FormItem> */}
{(isPercentState || isDecimalState) && (
<FormItem
label={
<FormItemTitle
title={'小数位数'}
subTitle={`对小数位数进行设置如保留两位0.021252 -> 2.12${
isPercentState ? '%' : ''
}`}
/>
}
name={['dataFormat', 'decimalPlaces']}
>
<InputNumber placeholder="请输入需要保留小数位数" style={{ width: '300px' }} />
</FormItem>
)}
{isPercentState && (
<>
<FormItem
label={
<FormItemTitle
title={'小数位数'}
subTitle={'对小数位数进行设置如保留两位0.021252 -> 2.12%'}
/>
}
name={['dataFormat', 'decimalPlaces']}
>
<InputNumber placeholder="请输入需要保留小数位数" style={{ width: '300px' }} />
</FormItem>
<FormItem
label={
<FormItemTitle
@@ -331,11 +432,24 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
form={form}
initialValues={{
...formValRef.current,
dataFormatType: '',
}}
onValuesChange={(value) => {
const { isPercent } = value;
if (isPercent !== undefined) {
setIsPercentState(isPercent);
onValuesChange={(value, values: any) => {
const { isPercent, dataFormatType } = values;
// if (isPercent !== undefined) {
// setIsPercentState(isPercent);
// }
if (dataFormatType === 'percent') {
setIsPercentState(true);
setIsDecimalState(false);
}
if (dataFormatType === 'decimal') {
setIsPercentState(false);
setIsDecimalState(true);
}
if (!dataFormatType) {
setIsPercentState(false);
setIsDecimalState(false);
}
}}
className={styles.form}
@@ -352,7 +466,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
type="primary"
key="console"
onClick={() => {
history.replace(`/semanticModel/${domainId}/${modelId}/dataSource`);
history.replace(`/model/${domainId}/${modelId}/dataSource`);
onCancel?.();
}}
>

View File

@@ -1,18 +1,19 @@
import { Space, Tooltip } from 'antd';
import React from 'react';
import React, { ReactNode } from 'react';
import { ExclamationCircleOutlined } from '@ant-design/icons';
type Props = {
title: string;
tooltips: string;
tooltips: string | ReactNode;
[key: string]: any;
};
const TableTitleTooltips: React.FC<Props> = ({ title, tooltips }) => {
const TableTitleTooltips: React.FC<Props> = ({ title, tooltips, ...rest }) => {
return (
<>
<Space>
<span>{title}</span>
<Tooltip title={tooltips}>
<Tooltip title={tooltips} {...rest}>
<ExclamationCircleOutlined />
</Tooltip>
</Space>

View File

@@ -31,16 +31,34 @@
.title {
margin-bottom: 0;
padding: 20px;
// padding: 20px;
font-size: 20px;
line-height: 34px;
border-bottom: 1px solid #d9d9d9;
display: flex;
}
.navContainer {
padding: 20px;
padding-left: 30px;
}
.backBtn {
background-color: #f8f8f8;
padding: 5px;
color: #b0b4bc;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
&:hover {
background-color: #7599e5;
color: #fff;
}
}
.tab {
:global {
.ant-tabs-tab-btn {
font-size: 16px !important;
font-size: 16px;
}
.ant-tabs-nav-wrap {
padding: 0 20px;
@@ -50,6 +68,16 @@
}
}
}
.chatSettingSectionTab {
:global {
.ant-tabs-tab-btn {
font-size: 14px;
}
.ant-tabs-nav-wrap {
padding: 0;
}
}
}
.mainTip {
padding: 20px;
@@ -60,9 +88,7 @@
padding: 0 !important;
}
.ant-tabs-content-holder {
margin-top: 20px;
// overflow: scroll;
// height: calc(100vh - 175px);
padding-top: 20px;
}
}
@@ -96,7 +122,7 @@
}
.domainTreeSelect {
width: 300px;
// width: 300px;
}
.domainList {

View File

@@ -197,6 +197,28 @@ export declare namespace ISemantic {
datasource: IDataSourceItem;
}
type IDomainSchemaRelaList = IDomainSchemaRelaItem[];
interface IDatabaseItem {
createdBy?: string;
updatedBy?: string;
createdAt?: string;
updatedAt?: string;
id: number;
name: string;
admins: string[];
type: string;
url: string;
username: string;
password: string;
version: string;
hasEditPermission: boolean;
hasUsePermission: boolean;
host: string;
port: string;
database?: string;
description?: string;
}
type IDatabaseItemList = IDatabaseItem[];
}
export declare namespace IChatConfig {

View File

@@ -1,7 +1,7 @@
import type { Reducer, Effect } from 'umi';
import { message } from 'antd';
import { ISemantic } from './data';
import { getDimensionList, queryMetric, excuteSql, getDatabaseByDomainId } from './service';
import { getDimensionList, queryMetric, excuteSql, getDatabaseList } from './service';
export type StateType = {
current: number;
@@ -14,7 +14,7 @@ export type StateType = {
metricList: ISemantic.IMetricList;
searchParams: Record<string, any>;
dataBaseResultColsMap: any;
dataBaseConfig: any;
databaseConfigList: any[];
domainData?: ISemantic.IDomainItem;
modelData?: ISemantic.IDomainItem;
domainList: ISemantic.IDomainItem[];
@@ -27,7 +27,7 @@ export type ModelType = {
queryDimensionList: Effect;
queryMetricList: Effect;
queryDataBaseExcuteSql: Effect;
queryDatabaseByDomainId: Effect;
queryDatabaseList: Effect;
};
reducers: {
setSelectDomain: Reducer<StateType>;
@@ -36,7 +36,7 @@ export type ModelType = {
setPagination: Reducer<StateType>;
setDimensionList: Reducer<StateType>;
setDataBaseScriptColumn: Reducer<StateType>;
setDataBaseConfig: Reducer<StateType>;
setDatabaseConfigList: Reducer<StateType>;
setMetricList: Reducer<StateType>;
reset: Reducer<StateType>;
};
@@ -55,7 +55,8 @@ export const defaultState: StateType = {
metricList: [],
domainData: undefined,
dataBaseResultColsMap: {},
dataBaseConfig: {},
databaseConfigList: [],
// dataBaseConfig: {},
domainList: [],
};
@@ -108,13 +109,12 @@ const Model: ModelType = {
message.error(msg);
}
},
*queryDatabaseByDomainId({ payload }, { call, put }) {
const domainId = payload.domainId;
const { code, data, msg } = yield call(getDatabaseByDomainId, domainId);
*queryDatabaseList({}, { call, put }) {
const { code, data, msg } = yield call(getDatabaseList);
if (code === 200) {
yield put({
type: 'setDataBaseConfig',
payload: { dataBaseConfig: data },
type: 'setDatabaseConfigList',
payload: { databaseConfigList: data },
});
} else {
message.error(msg);
@@ -171,7 +171,7 @@ const Model: ModelType = {
},
};
},
setDataBaseConfig(state = defaultState, action) {
setDatabaseConfigList(state = defaultState, action) {
return {
...state,
...action.payload,

View File

@@ -72,6 +72,18 @@ export function updateDimension(data: any): Promise<any> {
});
}
export function mockDimensionAlias(data: any): Promise<any> {
return request.post(`${process.env.API_BASE_URL}dimension/mockDimensionAlias`, {
data,
});
}
export function mockDimensionValuesAlias(data: any): Promise<any> {
return request.post(`${process.env.API_BASE_URL}dimension/mockDimensionValuesAlias`, {
data,
});
}
export function queryMetric(data: any): Promise<any> {
const { domainId, modelId } = data;
const queryParams = {
@@ -101,6 +113,12 @@ export function updateExprMetric(data: any): Promise<any> {
});
}
export function mockMetricAlias(data: any): Promise<any> {
return request.post(`${process.env.API_BASE_URL}metric/mockMetricAlias`, {
data,
});
}
export function getMeasureListByModelId(modelId: number): Promise<any> {
return request.get(`${process.env.API_BASE_URL}datasource/getMeasureListOfModel/${modelId}`);
}
@@ -216,12 +234,6 @@ export function deleteDatasourceRela(domainId: any): Promise<any> {
});
}
export function getDatabaseByDomainId(domainId: number): Promise<any> {
return request(`${process.env.API_BASE_URL}database/getDatabaseByDomainId/${domainId}`, {
method: 'GET',
});
}
export function getDomainSchemaRela(domainId: number): Promise<any> {
return request(`${process.env.API_BASE_URL}viewInfo/getDomainSchemaRela/${domainId}`, {
method: 'GET',
@@ -240,6 +252,18 @@ export type SaveDatabaseParams = {
description?: string;
};
export function getDatabaseList(): Promise<any> {
return request(`${process.env.API_BASE_URL}database/getDatabaseList`, {
method: 'GET',
});
}
export function deleteDatabase(domainId: any): Promise<any> {
return request(`${process.env.API_BASE_URL}database/${domainId}`, {
method: 'DELETE',
});
}
export function saveDatabase(data: SaveDatabaseParams): Promise<any> {
return request(`${process.env.API_BASE_URL}database/createOrUpdateDatabase`, {
method: 'POST',
@@ -256,7 +280,7 @@ export function testDatabaseConnect(data: SaveDatabaseParams): Promise<any> {
type ExcuteSqlParams = {
sql: string;
modelId: number;
id: number;
};
// 执行脚本