mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-10 11:07:06 +00:00
[improvement][semantic-fe] Refactor database settings functionality.
This commit is contained in:
@@ -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',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -11,7 +11,7 @@ type Props = {
|
||||
const ChatSetting: React.FC<Props> = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet title={'模型管理-超音数'} />
|
||||
<Helmet title={'语义模型-超音数'} />
|
||||
<OverviewContainer mode={'chatSetting'} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -29,10 +29,10 @@ export const TYPE_OPTIONS = [
|
||||
label: '主键',
|
||||
value: EnumDataSourceType.PRIMARY,
|
||||
},
|
||||
{
|
||||
label: '外键',
|
||||
value: EnumDataSourceType.FOREIGN,
|
||||
},
|
||||
// {
|
||||
// label: '外键',
|
||||
// value: EnumDataSourceType.FOREIGN,
|
||||
// },
|
||||
];
|
||||
|
||||
export const AGG_OPTIONS = [
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
// 数据类型
|
||||
export type DataInstanceItem = {
|
||||
sourceInstanceId: number; // 数据实例id
|
||||
sourceInstanceName: string; // 数据实例名
|
||||
defaultSourceId: number; // 查询表需要的默认datasource id
|
||||
bindSourceId: number;
|
||||
};
|
||||
|
||||
// 任务查询结果列
|
||||
export type TaskResultColumn = {
|
||||
name: string;
|
||||
|
||||
@@ -11,7 +11,7 @@ type Props = {
|
||||
const DomainManager: React.FC<Props> = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet title={'模型管理-超音数'} />
|
||||
<Helmet title={'语义模型-超音数'} />
|
||||
<OverviewContainer mode={'domain'} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 ? (
|
||||
|
||||
@@ -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,
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}}
|
||||
/>
|
||||
)} */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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(',') : [],
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
|
||||
@@ -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%' }}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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?.();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
// 执行脚本
|
||||
|
||||
Reference in New Issue
Block a user