[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

@@ -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 {