mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-11 03:58:14 +00:00
[improvement][semantic-fe] Added field type and metric type to the metric creation options. (#655)
* [improvement][semantic-fe] Add model alias setting & Add view permission restrictions to the model permission management tab. [improvement][semantic-fe] Add permission control to the action buttons for the main domain; apply high sensitivity filtering to the authorization of metrics/dimensions. [improvement][semantic-fe] Optimize the editing mode in the dimension/metric/datasource components to use the modelId stored in the database for data, instead of relying on the data from the state manager. * [improvement][semantic-fe] Add time granularity setting in the data source configuration. * [improvement][semantic-fe] Dictionary import for dimension values supported in Q&A visibility * [improvement][semantic-fe] Modification of data source creation prompt wording" * [improvement][semantic-fe] metric market experience optimization * [improvement][semantic-fe] enhance the analysis of metric trends * [improvement][semantic-fe] optimize the presentation of metric trend permissions * [improvement][semantic-fe] add metric trend download functionality * [improvement][semantic-fe] fix the dimension initialization issue in metric correlation * [improvement][semantic-fe] Fix the issue of database changes not taking effect when creating based on an SQL data source. * [improvement][semantic-fe] Optimizing pagination logic and some CSS styles * [improvement][semantic-fe] Fixing the API for the indicator list by changing "current" to "pageNum" * [improvement][semantic-fe] Fixing the default value setting for the indicator list * [improvement][semantic-fe] Adding batch operations for indicators/dimensions/models * [improvement][semantic-fe] Replacing the single status update API for indicators/dimensions with a batch update API * [improvement][semantic-fe] Redesigning the indicator homepage to incorporate trend charts and table functionality for indicators * [improvement][semantic-fe] Optimizing the logic for setting dimension values and editing data sources, and adding system settings functionality * [improvement][semantic-fe] Upgrading antd version to 5.x, extracting the batch operation button component, optimizing the interaction for system settings, and expanding the configuration generation types for list-to-select component. * [improvement][semantic-fe] Adding the ability to filter dimensions based on whether they are tags or not. * [improvement][semantic-fe] Adding the ability to edit relationships between models in the canvas. * [improvement][semantic-fe] Updating the datePicker component to use dayjs instead. * [improvement][semantic-fe] Fixing the issue with passing the model ID for dimensions in the indicator market. * [improvement][semantic-fe] Fixing the abnormal state of the popup when creating a model. * [improvement][semantic-fe] Adding permission logic for bulk operations in the indicator market. * [improvement][semantic-fe] Adding the ability to download and transpose data. * [improvement][semantic-fe] Fixing the initialization issue with the date selection component in the indicator details page when switching time granularity. * [improvement][semantic-fe] Fixing the logic error in the dimension value setting. * [improvement][semantic-fe] Fixing the synchronization issue with the question and answer settings information. * [improvement][semantic-fe] Optimizing the canvas functionality for better performance and user experience. * [improvement][semantic-fe] Optimizing the update process for drawing model relationship edges in the canvas. * [improvement][semantic-fe] Changing the line type for canvas connections. * [improvement][semantic-fe] Replacing the initialization variable from "semantic" to "headless". * [improvement][semantic-fe] Fixing the missing migration issue for default drill-down dimension configuration in model editing. Additionally, optimizing the data retrieval method for initializing fields in the model. * [improvement][semantic-fe] Updating the logic for the fieldName. * [improvement][semantic-fe] Adjusting the position of the metrics tab. * [improvement][semantic-fe] Changing the 字段名称 to 英文名称. * [improvement][semantic-fe] Fix metric measurement deletion. * [improvement][semantic-fe] UI optimization for metric details page. * [improvement][semantic-fe] UI optimization for metric details page. * [improvement][semantic-fe] UI adjustment for metric details page. * [improvement][semantic-fe] The granularity field in the time type of model editing now supports setting it as empty. * [improvement][semantic-fe] Added field type and metric type to the metric creation options.
This commit is contained in:
@@ -49,4 +49,21 @@ const settings = {
|
||||
'btn-disable-bg': 'rgba(0,10,36,0.04)',
|
||||
};
|
||||
|
||||
export const configProviderTheme = {
|
||||
components: {
|
||||
Button: {
|
||||
colorPrimary: '#3182ce',
|
||||
},
|
||||
Radio: {
|
||||
colorPrimary: '#3182ce',
|
||||
},
|
||||
Table: {
|
||||
headerBg: '#f9fafb',
|
||||
headerColor: '#667085',
|
||||
headerLineHeight: '38px',
|
||||
headerSplitColor: '#f9fafb',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Copilot } from 'supersonic-chat-sdk';
|
||||
import { getSystemConfig } from '@/services/user';
|
||||
export { request } from './services/request';
|
||||
import { ROUTE_AUTH_CODES } from '../config/routes';
|
||||
import { configProviderTheme } from '../config/themeSettings';
|
||||
|
||||
const replaceRoute = '/';
|
||||
|
||||
@@ -143,16 +144,8 @@ export const layout: RunTimeLayoutConfig = (params) => {
|
||||
menuHeaderRender: undefined,
|
||||
childrenRender: (dom: any) => {
|
||||
return (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Button: {
|
||||
colorPrimary: '#3182ce',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div
|
||||
<ConfigProvider theme={configProviderTheme}>
|
||||
<div
|
||||
style={{ height: location.pathname.includes('chat') ? 'calc(100vh - 56px)' : undefined }}
|
||||
>
|
||||
{dom}
|
||||
|
||||
@@ -17,16 +17,12 @@ const FormItemTitle: React.FC<IProps> = ({
|
||||
onSubTitleChange,
|
||||
}) => {
|
||||
return (
|
||||
// <div style={{ display: 'block' }}>
|
||||
|
||||
// </div>
|
||||
<Space direction="vertical" size={2} style={{ width: '100%' }}>
|
||||
<div>{title}</div>
|
||||
<div className={styles.subTitleContainer}>
|
||||
{subTitleEditable ? (
|
||||
<Paragraph
|
||||
editable={{
|
||||
// editing: true,
|
||||
onChange: (title: string) => {
|
||||
onSubTitleChange?.(title);
|
||||
},
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useModel } from 'umi';
|
||||
import HeaderDropdown from '../HeaderDropdown';
|
||||
import styles from './index.less';
|
||||
import TMEAvatar from '../TMEAvatar';
|
||||
import cx from 'classnames';
|
||||
import { AUTH_TOKEN_KEY } from '@/common/constants';
|
||||
import { history } from 'umi';
|
||||
|
||||
@@ -27,9 +26,7 @@ const { APP_TARGET } = process.env;
|
||||
|
||||
const AvatarDropdown: React.FC<GlobalHeaderRightProps> = () => {
|
||||
const { initialState = {}, setInitialState } = useModel('@@initialState');
|
||||
|
||||
const { currentUser = {} } = initialState as any;
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: (
|
||||
|
||||
@@ -9,7 +9,7 @@ import { updateModel, createModel, getColumns } from '../../service';
|
||||
import type { Dispatch } from 'umi';
|
||||
import type { StateType } from '../../model';
|
||||
import { connect } from 'umi';
|
||||
import { IDataSource, ISemantic } from '../../data';
|
||||
import { ISemantic, IDataSource } from '../../data';
|
||||
|
||||
export type CreateFormProps = {
|
||||
domainManger: StateType;
|
||||
@@ -34,7 +34,7 @@ const initFormVal = {
|
||||
description: '', // 模型描述
|
||||
};
|
||||
|
||||
const ModelCreateForm: React.FC<CreateFormProps> = ({
|
||||
const DataSourceCreateForm: React.FC<CreateFormProps> = ({
|
||||
domainManger,
|
||||
onCancel,
|
||||
createModalVisible,
|
||||
@@ -72,7 +72,9 @@ const ModelCreateForm: React.FC<CreateFormProps> = ({
|
||||
setHasEmptyNameField(hasEmpty);
|
||||
}, [fields]);
|
||||
|
||||
const [fieldColumns, setFieldColumns] = useState(scriptColumns || []);
|
||||
const [fieldColumns, setFieldColumns] = useState<IDataSource.IExecuteSqlColumn[]>(
|
||||
scriptColumns || [],
|
||||
);
|
||||
useEffect(() => {
|
||||
if (scriptColumns) {
|
||||
setFieldColumns(scriptColumns);
|
||||
@@ -162,6 +164,12 @@ const ModelCreateForm: React.FC<CreateFormProps> = ({
|
||||
...formValRef.current,
|
||||
...fieldsValue,
|
||||
...fieldsClassify,
|
||||
fields: fieldColumns.map((item) => {
|
||||
return {
|
||||
fieldName: item.nameEn,
|
||||
dataType: item.type,
|
||||
};
|
||||
}),
|
||||
};
|
||||
updateFormVal(submitForm);
|
||||
if (!saveState && currentStep < 1) {
|
||||
@@ -198,25 +206,22 @@ const ModelCreateForm: React.FC<CreateFormProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const initFields = (fieldsClassifyList: any[], columns: any[]) => {
|
||||
const initFields = (fieldsClassifyList: any[], columns: IDataSource.IExecuteSqlColumn[]) => {
|
||||
if (Array.isArray(columns) && columns.length === 0) {
|
||||
setFields(fieldsClassifyList || []);
|
||||
return;
|
||||
}
|
||||
const columnFields: any[] = columns.map((item: any) => {
|
||||
const columnFields: any[] = columns.map((item: IDataSource.IExecuteSqlColumn) => {
|
||||
const { type, nameEn } = item;
|
||||
const oldItem =
|
||||
fieldsClassifyList.find((oItem) => {
|
||||
// if (oItem.type === EnumDataSourceType.MEASURES) {
|
||||
// return oItem.expr === item.nameEn;
|
||||
// }
|
||||
return oItem.fieldName === item.nameEn;
|
||||
}) || {};
|
||||
return {
|
||||
...oldItem,
|
||||
bizName: nameEn,
|
||||
fieldName: nameEn,
|
||||
sqlType: type,
|
||||
dataType: type,
|
||||
};
|
||||
});
|
||||
setFields(columnFields || []);
|
||||
@@ -485,4 +490,4 @@ const ModelCreateForm: React.FC<CreateFormProps> = ({
|
||||
|
||||
export default connect(({ domainManger }: { domainManger: StateType }) => ({
|
||||
domainManger,
|
||||
}))(ModelCreateForm);
|
||||
}))(DataSourceCreateForm);
|
||||
|
||||
@@ -16,7 +16,7 @@ import styles from '../style.less';
|
||||
type FieldItem = {
|
||||
expr?: string;
|
||||
bizName: string;
|
||||
sqlType: string;
|
||||
dataType: string;
|
||||
name: string;
|
||||
type: EnumDataSourceType;
|
||||
agg?: string;
|
||||
@@ -47,7 +47,7 @@ const getCreateFieldName = (type: EnumDataSourceType) => {
|
||||
return isCreateName;
|
||||
};
|
||||
|
||||
const FieldForm: React.FC<Props> = ({ fields, sql, onFieldChange, onSqlChange }) => {
|
||||
const DataSourceFieldForm: React.FC<Props> = ({ fields, sql, onFieldChange, onSqlChange }) => {
|
||||
const handleFieldChange = (record: FieldItem, fieldName: string, value: any) => {
|
||||
onFieldChange(record.bizName, {
|
||||
...record,
|
||||
@@ -61,13 +61,13 @@ const FieldForm: React.FC<Props> = ({ fields, sql, onFieldChange, onSqlChange })
|
||||
dataIndex: 'fieldName',
|
||||
width: 100,
|
||||
},
|
||||
// {
|
||||
// title: '数据类型',
|
||||
// dataIndex: 'sqlType',
|
||||
// width: 80,
|
||||
// },
|
||||
{
|
||||
title: '字段类型',
|
||||
dataIndex: 'dataType',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '语义类型',
|
||||
dataIndex: 'type',
|
||||
width: 100,
|
||||
render: (_: any, record: FieldItem) => {
|
||||
@@ -305,9 +305,6 @@ const FieldForm: React.FC<Props> = ({ fields, sql, onFieldChange, onSqlChange })
|
||||
<div>
|
||||
为了保障同一个模型下维度/指标列表唯一,消除歧义,若本模型下的多个数据源存在相同的字段名并且都勾选了快速创建,系统默认这些相同字段的指标维度是同一个,同时列表中将只显示第一次创建的指标/维度。
|
||||
</div>
|
||||
// <Marquee pauseOnHover gradient={false}>
|
||||
// 为了保障同一个主题域下维度/指标列表唯一,消除歧义,若本主题域下的多个数据源存在相同的字段名并且都勾选了快速创建,系统默认这些相同字段的指标维度是同一个,同时列表中将只显示最后一次创建的指标/维度。
|
||||
// </Marquee>
|
||||
}
|
||||
/>
|
||||
<Table<FieldItem>
|
||||
@@ -329,4 +326,4 @@ const FieldForm: React.FC<Props> = ({ fields, sql, onFieldChange, onSqlChange })
|
||||
);
|
||||
};
|
||||
|
||||
export default FieldForm;
|
||||
export default DataSourceFieldForm;
|
||||
|
||||
@@ -22,6 +22,7 @@ import styles from './style.less';
|
||||
import { ISemantic } from '../data';
|
||||
import BatchCtrlDropDownButton from '@/components/BatchCtrlDropDownButton';
|
||||
import MetricStar from './components/MetricStar';
|
||||
import { ColumnsConfig } from '../components/MetricTableColumnRender';
|
||||
|
||||
type Props = {
|
||||
dispatch: Dispatch;
|
||||
@@ -166,22 +167,8 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
},
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: '指标名称',
|
||||
render: (_, record: any) => {
|
||||
const { id, isCollect } = record;
|
||||
return (
|
||||
<Space>
|
||||
<MetricStar metricId={id} initState={isCollect} />
|
||||
<a
|
||||
onClick={() => {
|
||||
history.push(`/metric/detail/${record.id}`);
|
||||
}}
|
||||
>
|
||||
{record.name}
|
||||
</a>
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
title: '指标',
|
||||
render: ColumnsConfig.metricInfo.render,
|
||||
},
|
||||
{
|
||||
dataIndex: 'modelName',
|
||||
@@ -203,54 +190,12 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
return <> {record.modelName}</>;
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'sensitiveLevel',
|
||||
title: '敏感度',
|
||||
valueEnum: SENSITIVE_LEVEL_ENUM,
|
||||
},
|
||||
{
|
||||
dataIndex: 'status',
|
||||
title: '状态',
|
||||
width: 80,
|
||||
search: false,
|
||||
render: (status) => {
|
||||
switch (status) {
|
||||
case StatusEnum.ONLINE:
|
||||
return <Tag color="success">已启用</Tag>;
|
||||
case StatusEnum.OFFLINE:
|
||||
return <Tag color="warning">未启用</Tag>;
|
||||
case StatusEnum.INITIALIZED:
|
||||
return <Tag color="processing">初始化</Tag>;
|
||||
case StatusEnum.DELETED:
|
||||
return <Tag color="default">已删除</Tag>;
|
||||
default:
|
||||
return <Tag color="default">未知</Tag>;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'createdBy',
|
||||
title: '创建人',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
dataIndex: 'tags',
|
||||
title: '标签',
|
||||
search: false,
|
||||
render: (tags) => {
|
||||
if (Array.isArray(tags)) {
|
||||
return (
|
||||
<Space size={2}>
|
||||
{tags.map((tag) => (
|
||||
<Tag color="blue" key={tag}>
|
||||
{tag}
|
||||
</Tag>
|
||||
))}
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
return <>--</>;
|
||||
},
|
||||
render: ColumnsConfig.state.render,
|
||||
},
|
||||
{
|
||||
dataIndex: 'description',
|
||||
@@ -403,6 +348,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
dataSource={dataSource}
|
||||
columns={columns}
|
||||
pagination={pagination}
|
||||
size="large"
|
||||
tableAlertRender={() => {
|
||||
return false;
|
||||
}}
|
||||
@@ -435,7 +381,6 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
setPagination(pagin);
|
||||
queryMetricList({ ...pagin, ...filterParams });
|
||||
}}
|
||||
size="small"
|
||||
options={{ reload: false, density: false, fullScreen: false }}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -74,7 +74,7 @@ const BindMeasuresTable: React.FC<CreateFormProps> = ({
|
||||
<>
|
||||
<Button onClick={onCancel}>取消</Button>
|
||||
<Button type="primary" onClick={handleSubmit}>
|
||||
将选中度量添加到指标
|
||||
添加度量
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -40,7 +40,7 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
|
||||
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState<boolean>(false);
|
||||
const [dataSourceEditOpen, setDataSourceEditOpen] = useState<boolean>(false);
|
||||
const [currentDatabaseId, setCurrentDatabaseId] = useState<number>();
|
||||
const [scriptColumns, setScriptColumns] = useState<any[]>([]);
|
||||
const [scriptColumns, setScriptColumns] = useState<IDataSource.IExecuteSqlColumn[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dataSourceItem?.id || !open) {
|
||||
@@ -74,24 +74,36 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
|
||||
useEffect(() => {
|
||||
// queryTableColumnListByScript(dataSourceItem);
|
||||
setSql(dataSourceItem?.modelDetail?.sqlQuery);
|
||||
|
||||
const modelDetailFields = dataSourceItem?.modelDetail?.fields;
|
||||
if (Array.isArray(modelDetailFields)) {
|
||||
setScriptColumns(
|
||||
modelDetailFields.map((item) => {
|
||||
return {
|
||||
nameEn: item.fieldName,
|
||||
type: item.dataType,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
}, [dataSourceItem]);
|
||||
|
||||
const fetchTaskResult = (params) => {
|
||||
setScriptColumns(params.columns);
|
||||
};
|
||||
// const fetchTaskResult = (params) => {
|
||||
// setScriptColumns(params.columns);
|
||||
// };
|
||||
|
||||
const queryTableColumnListByScript = async (dataSource: IDataSource.IDataSourceItem) => {
|
||||
if (!dataSource?.modelDetail?.sqlQuery) {
|
||||
return;
|
||||
}
|
||||
const { code, data } = await excuteSql({
|
||||
sql: dataSource.modelDetail?.sqlQuery,
|
||||
id: dataSource.databaseId,
|
||||
});
|
||||
if (code === 200) {
|
||||
fetchTaskResult(data);
|
||||
}
|
||||
};
|
||||
// const queryTableColumnListByScript = async (dataSource: IDataSource.IDataSourceItem) => {
|
||||
// if (!dataSource?.modelDetail?.sqlQuery) {
|
||||
// return;
|
||||
// }
|
||||
// const { code, data } = await excuteSql({
|
||||
// sql: dataSource.modelDetail?.sqlQuery,
|
||||
// id: dataSource.databaseId,
|
||||
// });
|
||||
// if (code === 200) {
|
||||
// fetchTaskResult(data);
|
||||
// }
|
||||
// };
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -38,9 +38,9 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
>([]);
|
||||
const [dimensionValueSettingModalVisible, setDimensionValueSettingModalVisible] =
|
||||
useState<boolean>(false);
|
||||
const [pagination, setPagination] = useState({
|
||||
const [pagination] = useState({
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
pageSize: 99999,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
@@ -54,16 +54,9 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
modelId,
|
||||
});
|
||||
setLoading(false);
|
||||
const { list, pageSize, pageNum, total } = data || {};
|
||||
const { list } = data || {};
|
||||
let resData: any = {};
|
||||
if (code === 200) {
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageSize: Math.min(pageSize, 100),
|
||||
current: pageNum,
|
||||
total,
|
||||
});
|
||||
|
||||
resData = {
|
||||
data: list || [],
|
||||
success: true,
|
||||
@@ -327,30 +320,6 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
},
|
||||
};
|
||||
|
||||
// const dropdownButtonItems = [
|
||||
// {
|
||||
// key: 'batchStart',
|
||||
// label: '批量启用',
|
||||
// },
|
||||
// {
|
||||
// key: 'batchStop',
|
||||
// label: '批量停用',
|
||||
// },
|
||||
// {
|
||||
// key: 'batchDelete',
|
||||
// label: (
|
||||
// <Popconfirm
|
||||
// title="确定批量删除吗?"
|
||||
// onConfirm={() => {
|
||||
// queryBatchUpdateStatus(selectedRowKeys, StatusEnum.DELETED);
|
||||
// }}
|
||||
// >
|
||||
// <a>批量删除</a>
|
||||
// </Popconfirm>
|
||||
// ),
|
||||
// },
|
||||
// ];
|
||||
|
||||
const onMenuClick = (key: string) => {
|
||||
switch (key) {
|
||||
case 'batchStart':
|
||||
@@ -372,10 +341,8 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
request={queryDimensionList}
|
||||
pagination={pagination}
|
||||
loading={loading}
|
||||
search={{
|
||||
span: 4,
|
||||
defaultCollapsed: false,
|
||||
collapseRender: () => {
|
||||
return <></>;
|
||||
@@ -385,14 +352,6 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
type: 'checkbox',
|
||||
...rowSelection,
|
||||
}}
|
||||
onChange={(data: any) => {
|
||||
const { current, pageSize, total } = data;
|
||||
setPagination({
|
||||
current,
|
||||
pageSize,
|
||||
total,
|
||||
});
|
||||
}}
|
||||
tableAlertRender={() => {
|
||||
return false;
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ActionType, ProColumns } from '@ant-design/pro-table';
|
||||
import ProTable from '@ant-design/pro-table';
|
||||
import { message, Button, Space, Popconfirm, Input, Tag } from 'antd';
|
||||
import { message, Button, Space, Popconfirm, Input } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import type { Dispatch } from 'umi';
|
||||
import { StatusEnum } from '../enum';
|
||||
@@ -19,6 +19,7 @@ import BatchCtrlDropDownButton from '@/components/BatchCtrlDropDownButton';
|
||||
import moment from 'moment';
|
||||
import styles from './style.less';
|
||||
import { ISemantic } from '../data';
|
||||
import { ColumnsConfig } from './MetricTableColumnRender';
|
||||
|
||||
type Props = {
|
||||
dispatch: Dispatch;
|
||||
@@ -92,16 +93,12 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
};
|
||||
|
||||
const columns: ProColumns[] = [
|
||||
{
|
||||
dataIndex: 'id',
|
||||
title: 'ID',
|
||||
width: 80,
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: '指标名称',
|
||||
title: '指标',
|
||||
width: '30%',
|
||||
search: false,
|
||||
render: ColumnsConfig.metricInfo.render,
|
||||
},
|
||||
{
|
||||
dataIndex: 'key',
|
||||
@@ -109,74 +106,25 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
hideInTable: true,
|
||||
renderFormItem: () => <Input placeholder="请输入ID/指标名称/英文名称/标签" />,
|
||||
},
|
||||
{
|
||||
dataIndex: 'alias',
|
||||
title: '别名',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
dataIndex: 'bizName',
|
||||
title: '英文名称',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
dataIndex: 'sensitiveLevel',
|
||||
title: '敏感度',
|
||||
width: 80,
|
||||
hideInTable: true,
|
||||
valueEnum: SENSITIVE_LEVEL_ENUM,
|
||||
},
|
||||
{
|
||||
dataIndex: 'status',
|
||||
title: '状态',
|
||||
width: 80,
|
||||
search: false,
|
||||
render: (status) => {
|
||||
switch (status) {
|
||||
case StatusEnum.ONLINE:
|
||||
return <Tag color="success">已启用</Tag>;
|
||||
case StatusEnum.OFFLINE:
|
||||
return <Tag color="warning">未启用</Tag>;
|
||||
case StatusEnum.INITIALIZED:
|
||||
return <Tag color="processing">初始化</Tag>;
|
||||
case StatusEnum.DELETED:
|
||||
return <Tag color="default">已删除</Tag>;
|
||||
default:
|
||||
return <Tag color="default">未知</Tag>;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'createdBy',
|
||||
title: '创建人',
|
||||
width: 100,
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
dataIndex: 'tags',
|
||||
title: '标签',
|
||||
search: false,
|
||||
render: (tags) => {
|
||||
if (Array.isArray(tags)) {
|
||||
return (
|
||||
<Space size={2} wrap>
|
||||
{tags.map((tag) => (
|
||||
<Tag color="blue" key={tag}>
|
||||
{tag}
|
||||
</Tag>
|
||||
))}
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
return <>--</>;
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'description',
|
||||
title: '描述',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
dataIndex: 'status',
|
||||
title: '状态',
|
||||
width: 200,
|
||||
search: false,
|
||||
render: ColumnsConfig.state.render,
|
||||
},
|
||||
|
||||
{
|
||||
dataIndex: 'updatedAt',
|
||||
title: '更新时间',
|
||||
@@ -302,11 +250,8 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
search={{
|
||||
span: 4,
|
||||
defaultCollapsed: false,
|
||||
collapseRender: () => {
|
||||
return <></>;
|
||||
},
|
||||
optionRender: false,
|
||||
collapsed: false,
|
||||
}}
|
||||
rowSelection={{
|
||||
type: 'checkbox',
|
||||
@@ -327,7 +272,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
total,
|
||||
});
|
||||
}}
|
||||
size="small"
|
||||
size="large"
|
||||
options={{ reload: false, density: false, fullScreen: false }}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import { useEffect, forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import type { ForwardRefRenderFunction } from 'react';
|
||||
import { message, Form, Input, Select, Button, Space } from 'antd';
|
||||
import { saveDatabase, testDatabaseConnect } from '../../service';
|
||||
import {
|
||||
saveDatabase,
|
||||
testDatabaseConnect,
|
||||
getDatabaseParameters,
|
||||
getDatabaseDetail,
|
||||
} from '../../service';
|
||||
import { formLayout } from '@/components/FormHelper/utils';
|
||||
import SelectTMEPerson from '@/components/SelectTMEPerson';
|
||||
import { ConfigParametersItem } from '../../../System/types';
|
||||
import { genneratorFormItemList } from '../../utils';
|
||||
import { ISemantic } from '../../data';
|
||||
|
||||
import styles from '../style.less';
|
||||
type Props = {
|
||||
domainId?: number;
|
||||
dataBaseConfig?: ISemantic.IDatabaseItem;
|
||||
databaseId?: number;
|
||||
hideSubmitBtn?: boolean;
|
||||
onSubmit?: (params?: any) => void;
|
||||
};
|
||||
@@ -18,21 +25,59 @@ const FormItem = Form.Item;
|
||||
const TextArea = Input.TextArea;
|
||||
|
||||
const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
{ domainId, dataBaseConfig, onSubmit, hideSubmitBtn = false },
|
||||
{ domainId, databaseId, onSubmit, hideSubmitBtn = false },
|
||||
ref,
|
||||
) => {
|
||||
const [form] = Form.useForm();
|
||||
const [selectedDbType, setSelectedDbType] = useState<string>('h2');
|
||||
|
||||
const [selectedDbType, setSelectedDbType] = useState<string>('');
|
||||
const [databaseOptions, setDatabaseOptions] = useState<{ value: string; label: string }[]>([]);
|
||||
const [databaseConfig, setDatabaseConfig] = useState<Record<string, ConfigParametersItem[]>>({});
|
||||
const [testLoading, setTestLoading] = useState<boolean>(false);
|
||||
|
||||
const [dataBaseDetail, setDataBaseDetail] = useState<ISemantic.IDatabaseItem>();
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
if (dataBaseConfig) {
|
||||
form.setFieldsValue({ ...dataBaseConfig });
|
||||
setSelectedDbType(dataBaseConfig?.type);
|
||||
if (dataBaseDetail) {
|
||||
form.setFieldsValue({ ...dataBaseDetail });
|
||||
setSelectedDbType(dataBaseDetail?.type);
|
||||
}
|
||||
}, [dataBaseConfig]);
|
||||
}, [dataBaseDetail]);
|
||||
|
||||
useEffect(() => {
|
||||
if (databaseId) {
|
||||
queryDatabaseDetail(databaseId);
|
||||
}
|
||||
}, [databaseId]);
|
||||
|
||||
useEffect(() => {
|
||||
queryDatabaseConfig();
|
||||
}, []);
|
||||
|
||||
const queryDatabaseDetail = async (id: number) => {
|
||||
const { code, msg, data } = await getDatabaseDetail(id);
|
||||
if (code === 200) {
|
||||
setDataBaseDetail(data);
|
||||
return;
|
||||
}
|
||||
message.error(msg);
|
||||
};
|
||||
|
||||
const queryDatabaseConfig = async () => {
|
||||
const { code, msg, data } = await getDatabaseParameters();
|
||||
if (code === 200) {
|
||||
const options = Object.keys(data).map((sqlName: string) => {
|
||||
return {
|
||||
value: sqlName,
|
||||
label: sqlName,
|
||||
};
|
||||
});
|
||||
setDatabaseConfig(data);
|
||||
setDatabaseOptions(options);
|
||||
return;
|
||||
}
|
||||
message.error(msg);
|
||||
};
|
||||
|
||||
const getFormValidateFields = async () => {
|
||||
return await form.validateFields();
|
||||
@@ -47,7 +92,7 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
const saveDatabaseConfig = async () => {
|
||||
const values = await form.validateFields();
|
||||
const { code, msg } = await saveDatabase({
|
||||
...dataBaseConfig,
|
||||
...(dataBaseDetail || {}),
|
||||
...values,
|
||||
domainId,
|
||||
});
|
||||
@@ -98,14 +143,12 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
placeholder="请选择数据库类型"
|
||||
options={[
|
||||
{ value: 'h2', label: 'h2' },
|
||||
{ value: 'mysql', label: 'mysql' },
|
||||
{ value: 'clickhouse', label: 'clickhouse' },
|
||||
]}
|
||||
options={databaseOptions}
|
||||
/>
|
||||
</FormItem>
|
||||
{selectedDbType === 'h2' ? (
|
||||
|
||||
{databaseConfig[selectedDbType] && genneratorFormItemList(databaseConfig[selectedDbType])}
|
||||
{/* {selectedDbType === 'h2' ? (
|
||||
<FormItem name="url" label="链接" rules={[{ required: true, message: '请输入链接' }]}>
|
||||
<Input placeholder="请输入链接" />
|
||||
</FormItem>
|
||||
@@ -139,11 +182,10 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
]}
|
||||
/>
|
||||
</FormItem>
|
||||
)}
|
||||
<FormItem
|
||||
)} */}
|
||||
{/* <FormItem
|
||||
name="username"
|
||||
label="用户名"
|
||||
// rules={[{ required: true, message: '请输入用户名' }]}
|
||||
>
|
||||
<Input placeholder="请输入用户名" />
|
||||
</FormItem>
|
||||
@@ -152,7 +194,7 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
</FormItem>
|
||||
<FormItem name="database" label="数据库名称">
|
||||
<Input placeholder="请输入数据库名称" />
|
||||
</FormItem>
|
||||
</FormItem> */}
|
||||
<FormItem
|
||||
name="admins"
|
||||
label="管理员"
|
||||
|
||||
@@ -55,7 +55,7 @@ const DatabaseSettingModal: React.FC<CreateFormProps> = ({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
width={1200}
|
||||
width={600}
|
||||
destroyOnClose
|
||||
title="数据库连接设置"
|
||||
style={{ top: 48 }}
|
||||
@@ -67,7 +67,8 @@ const DatabaseSettingModal: React.FC<CreateFormProps> = ({
|
||||
<DatabaseCreateForm
|
||||
hideSubmitBtn={true}
|
||||
ref={createFormRef}
|
||||
dataBaseConfig={databaseItem}
|
||||
// dataBaseConfig={databaseItem}
|
||||
databaseId={databaseItem?.id}
|
||||
onSubmit={() => {
|
||||
onSubmit?.();
|
||||
}}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import { Modal, Button, message } from 'antd';
|
||||
import DimensionMetricRelationTableTransfer from './DimensionMetricRelationTableTransfer';
|
||||
import { ISemantic } from '../data';
|
||||
import { updateExprMetric } from '../service';
|
||||
import { updateMetric } from '../service';
|
||||
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||
|
||||
type Props = {
|
||||
@@ -15,7 +15,7 @@ type Props = {
|
||||
|
||||
const DimensionAndMetricRelationModal: React.FC<Props> = ({
|
||||
open,
|
||||
metricItem = {},
|
||||
metricItem,
|
||||
relationsInitialValue,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
@@ -30,7 +30,7 @@ const DimensionAndMetricRelationModal: React.FC<Props> = ({
|
||||
drillDownDimensions: relationList,
|
||||
},
|
||||
};
|
||||
const { code, msg } = await updateExprMetric(queryParams);
|
||||
const { code, msg } = await updateMetric(queryParams);
|
||||
if (code === 200) {
|
||||
onSubmit(relationList);
|
||||
return;
|
||||
@@ -45,7 +45,11 @@ const DimensionAndMetricRelationModal: React.FC<Props> = ({
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
saveMetric(relationList);
|
||||
if (metricItem?.id) {
|
||||
saveMetric(relationList);
|
||||
} else {
|
||||
onSubmit(relationList);
|
||||
}
|
||||
}}
|
||||
>
|
||||
完成
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Space } from 'antd';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
@@ -15,7 +16,12 @@ const FormLabelRequire: React.FC<Props> = ({ title, labelStyles = {} }) => {
|
||||
title={title}
|
||||
style={{ fontSize: '16px', ...labelStyles }}
|
||||
>
|
||||
{title}
|
||||
<Space size={5}>
|
||||
<span style={{ color: '#ff4d4f', fontSize: '18px', position: 'relative', top: 3 }}>
|
||||
*
|
||||
</span>
|
||||
{title}
|
||||
</Space>
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -15,19 +15,23 @@ import {
|
||||
Col,
|
||||
Space,
|
||||
Tooltip,
|
||||
Tag,
|
||||
} from 'antd';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import MetricMeasuresFormTable from './MetricMeasuresFormTable';
|
||||
import { SENSITIVE_LEVEL_OPTIONS } from '../constant';
|
||||
import { SENSITIVE_LEVEL_OPTIONS, METRIC_DEFINE_TYPE } from '../constant';
|
||||
import { formLayout } from '@/components/FormHelper/utils';
|
||||
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||
import styles from './style.less';
|
||||
import { getMeasureListByModelId, getModelDetail } from '../service';
|
||||
import { getMetricsToCreateNewMetric, getModelDetail } from '../service';
|
||||
import MetricMetricFormTable from './MetricMetricFormTable';
|
||||
import MetricFieldFormTable from './MetricFieldFormTable';
|
||||
import DimensionAndMetricRelationModal from './DimensionAndMetricRelationModal';
|
||||
import TableTitleTooltips from '../components/TableTitleTooltips';
|
||||
import { creatExprMetric, updateExprMetric, mockMetricAlias, getMetricTags } from '../service';
|
||||
import { createMetric, updateMetric, mockMetricAlias, getMetricTags } from '../service';
|
||||
import { ISemantic } from '../data';
|
||||
import { history } from 'umi';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
export type CreateFormProps = {
|
||||
datasourceId?: number;
|
||||
@@ -44,6 +48,12 @@ const FormItem = Form.Item;
|
||||
const { TextArea } = Input;
|
||||
const { Option } = Select;
|
||||
|
||||
const queryParamsTypeParamsKey = {
|
||||
[METRIC_DEFINE_TYPE.MEASURE]: 'metricDefineByMeasureParams',
|
||||
[METRIC_DEFINE_TYPE.METRIC]: 'metricDefineByMetricParams',
|
||||
[METRIC_DEFINE_TYPE.FIELD]: 'metricDefineByFieldParams',
|
||||
};
|
||||
|
||||
const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
datasourceId,
|
||||
domainId,
|
||||
@@ -67,10 +77,31 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
|
||||
const [classMeasureList, setClassMeasureList] = useState<ISemantic.IMeasure[]>([]);
|
||||
|
||||
const [exprTypeParamsState, setExprTypeParamsState] = useState<ISemantic.IMeasure[]>([]);
|
||||
const [exprTypeParamsState, setExprTypeParamsState] = useState<{
|
||||
[METRIC_DEFINE_TYPE.MEASURE]: ISemantic.IMeasureTypeParams;
|
||||
[METRIC_DEFINE_TYPE.METRIC]: ISemantic.IMetricTypeParams;
|
||||
[METRIC_DEFINE_TYPE.FIELD]: ISemantic.IFieldTypeParams;
|
||||
}>({
|
||||
[METRIC_DEFINE_TYPE.MEASURE]: {
|
||||
measures: [],
|
||||
expr: '',
|
||||
},
|
||||
[METRIC_DEFINE_TYPE.METRIC]: {
|
||||
metrics: [],
|
||||
expr: '',
|
||||
},
|
||||
[METRIC_DEFINE_TYPE.FIELD]: {
|
||||
fields: [],
|
||||
expr: '',
|
||||
},
|
||||
} as any);
|
||||
|
||||
const [exprSql, setExprSql] = useState<string>('');
|
||||
// const [exprTypeParamsState, setExprTypeParamsState] = useState<ISemantic.IMeasure[]>([]);
|
||||
|
||||
const [defineType, setDefineType] = useState(METRIC_DEFINE_TYPE.MEASURE);
|
||||
|
||||
const [createNewMetricList, setCreateNewMetricList] = useState<ISemantic.IMetricItem[]>([]);
|
||||
const [fieldList, setFieldList] = useState<string[]>([]);
|
||||
const [isPercentState, setIsPercentState] = useState<boolean>(false);
|
||||
const [isDecimalState, setIsDecimalState] = useState<boolean>(false);
|
||||
const [hasMeasuresState, setHasMeasuresState] = useState<boolean>(true);
|
||||
@@ -87,10 +118,13 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
const forward = () => setCurrentStep(currentStep + 1);
|
||||
const backward = () => setCurrentStep(currentStep - 1);
|
||||
|
||||
const queryClassMeasureList = async () => {
|
||||
const queryModelDetail = async () => {
|
||||
// const { code, data } = await getMeasureListByModelId(modelId);
|
||||
const { code, data } = await getModelDetail({ modelId: modelId || metricItem?.modelId });
|
||||
if (code === 200) {
|
||||
if (Array.isArray(data?.modelDetail?.fields)) {
|
||||
setFieldList(data.modelDetail.fields);
|
||||
}
|
||||
if (Array.isArray(data?.modelDetail?.measures)) {
|
||||
setClassMeasureList(data.modelDetail.measures);
|
||||
if (datasourceId) {
|
||||
@@ -106,7 +140,8 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
queryClassMeasureList();
|
||||
queryModelDetail();
|
||||
queryMetricsToCreateNewMetric();
|
||||
queryMetricTags();
|
||||
}, []);
|
||||
|
||||
@@ -115,10 +150,8 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
const submitForm = {
|
||||
...formValRef.current,
|
||||
...fieldsValue,
|
||||
typeParams: {
|
||||
expr: exprSql,
|
||||
measures: exprTypeParamsState,
|
||||
},
|
||||
metricDefineType: defineType,
|
||||
[queryParamsTypeParamsKey[defineType]]: exprTypeParamsState[defineType],
|
||||
};
|
||||
updateFormVal(submitForm);
|
||||
if (currentStep < 1) {
|
||||
@@ -135,12 +168,16 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
bizName,
|
||||
description,
|
||||
sensitiveLevel,
|
||||
typeParams: typeParams,
|
||||
typeParams,
|
||||
dataFormat,
|
||||
dataFormatType,
|
||||
alias,
|
||||
tags,
|
||||
} = metricItem as any;
|
||||
metricDefineType,
|
||||
metricDefineByMeasureParams,
|
||||
metricDefineByMetricParams,
|
||||
metricDefineByFieldParams,
|
||||
} = metricItem;
|
||||
const isPercent = dataFormatType === 'percent';
|
||||
const isDecimal = dataFormatType === 'decimal';
|
||||
const initValue = {
|
||||
@@ -162,10 +199,39 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
...formValRef.current,
|
||||
...initValue,
|
||||
};
|
||||
if (metricDefineType === METRIC_DEFINE_TYPE.MEASURE) {
|
||||
const { measures, expr } = metricDefineByMeasureParams || {};
|
||||
setExprTypeParamsState({
|
||||
...exprTypeParamsState,
|
||||
[METRIC_DEFINE_TYPE.MEASURE]: {
|
||||
measures: measures || [],
|
||||
expr: expr || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
if (metricDefineType === METRIC_DEFINE_TYPE.METRIC) {
|
||||
const { metrics, expr } = metricDefineByMetricParams || {};
|
||||
setExprTypeParamsState({
|
||||
...exprTypeParamsState,
|
||||
[METRIC_DEFINE_TYPE.METRIC]: {
|
||||
metrics: metrics || [],
|
||||
expr: expr || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
if (metricDefineType === METRIC_DEFINE_TYPE.FIELD) {
|
||||
const { fields, expr } = metricDefineByFieldParams || {};
|
||||
setExprTypeParamsState({
|
||||
...exprTypeParamsState,
|
||||
[METRIC_DEFINE_TYPE.FIELD]: {
|
||||
fields: fields || [],
|
||||
expr: expr || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
updateFormVal(editInitFormVal);
|
||||
form.setFieldsValue(initValue);
|
||||
setExprTypeParamsState(typeParams.measures);
|
||||
setExprSql(typeParams.expr);
|
||||
setDefineType(metricDefineType);
|
||||
setIsPercentState(isPercent);
|
||||
setIsDecimalState(isDecimal);
|
||||
};
|
||||
@@ -176,6 +242,37 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
}
|
||||
}, [metricItem]);
|
||||
|
||||
const isEmptyConditions = (
|
||||
metricDefineType: METRIC_DEFINE_TYPE,
|
||||
metricDefineParams:
|
||||
| ISemantic.IMeasureTypeParams
|
||||
| ISemantic.IMetricTypeParams
|
||||
| ISemantic.IFieldTypeParams,
|
||||
) => {
|
||||
if (metricDefineType === METRIC_DEFINE_TYPE.MEASURE) {
|
||||
const { measures } = (metricDefineParams as ISemantic.IMeasureTypeParams) || {};
|
||||
if (!(Array.isArray(measures) && measures.length > 0)) {
|
||||
message.error('请添加一个度量');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (metricDefineType === METRIC_DEFINE_TYPE.METRIC) {
|
||||
const { metrics } = (metricDefineParams as ISemantic.IMetricTypeParams) || {};
|
||||
if (!(Array.isArray(metrics) && metrics.length > 0)) {
|
||||
message.error('请添加一个指标');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (metricDefineType === METRIC_DEFINE_TYPE.FIELD) {
|
||||
const { fields } = (metricDefineParams as ISemantic.IFieldTypeParams) || {};
|
||||
if (!(Array.isArray(fields) && fields.length > 0)) {
|
||||
message.error('请添加一个字段');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const saveMetric = async (fieldsValue: any) => {
|
||||
const queryParams = {
|
||||
modelId: isEdit ? metricItem.modelId : modelId,
|
||||
@@ -185,22 +282,22 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
},
|
||||
...fieldsValue,
|
||||
};
|
||||
const { typeParams, alias, dataFormatType } = queryParams;
|
||||
const { alias, dataFormatType } = queryParams;
|
||||
queryParams.alias = Array.isArray(alias) ? alias.join(',') : '';
|
||||
if (!typeParams?.expr) {
|
||||
if (!queryParams[queryParamsTypeParamsKey[defineType]]?.expr) {
|
||||
message.error('请输入度量表达式');
|
||||
return;
|
||||
}
|
||||
if (!dataFormatType) {
|
||||
delete queryParams.dataFormat;
|
||||
}
|
||||
if (!(Array.isArray(typeParams?.measures) && typeParams.measures.length > 0)) {
|
||||
message.error('请添加一个度量');
|
||||
if (isEmptyConditions(defineType, queryParams[queryParamsTypeParamsKey[defineType]])) {
|
||||
return;
|
||||
}
|
||||
let saveMetricQuery = creatExprMetric;
|
||||
|
||||
let saveMetricQuery = createMetric;
|
||||
if (queryParams.id) {
|
||||
saveMetricQuery = updateExprMetric;
|
||||
saveMetricQuery = updateMetric;
|
||||
}
|
||||
const { code, msg } = await saveMetricQuery(queryParams);
|
||||
if (code === 200) {
|
||||
@@ -238,24 +335,141 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
message.error('获取指标标签失败');
|
||||
}
|
||||
};
|
||||
const queryMetricsToCreateNewMetric = async () => {
|
||||
const { code, data } = await getMetricsToCreateNewMetric({ modelId });
|
||||
if (code === 200) {
|
||||
setCreateNewMetricList(data);
|
||||
} else {
|
||||
message.error('获取指标标签失败');
|
||||
}
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
if (currentStep === 1) {
|
||||
return (
|
||||
<MetricMeasuresFormTable
|
||||
datasourceId={datasourceId}
|
||||
typeParams={{
|
||||
measures: exprTypeParamsState,
|
||||
expr: exprSql,
|
||||
}}
|
||||
measuresList={classMeasureList}
|
||||
onFieldChange={(typeParams: any) => {
|
||||
setExprTypeParamsState([...typeParams]);
|
||||
}}
|
||||
onSqlChange={(sql: string) => {
|
||||
setExprSql(sql);
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
padding: '0 0 20px 24px',
|
||||
// borderBottom: '1px solid #eee',
|
||||
}}
|
||||
>
|
||||
<Radio.Group
|
||||
buttonStyle="solid"
|
||||
value={defineType}
|
||||
onChange={(e) => {
|
||||
setDefineType(e.target.value);
|
||||
}}
|
||||
>
|
||||
<Radio.Button value={METRIC_DEFINE_TYPE.MEASURE}>按度量</Radio.Button>
|
||||
<Radio.Button value={METRIC_DEFINE_TYPE.METRIC}>按指标</Radio.Button>
|
||||
<Radio.Button value={METRIC_DEFINE_TYPE.FIELD}>按字段</Radio.Button>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
{defineType === METRIC_DEFINE_TYPE.MEASURE && (
|
||||
<>
|
||||
<MetricMeasuresFormTable
|
||||
datasourceId={datasourceId}
|
||||
typeParams={exprTypeParamsState[METRIC_DEFINE_TYPE.MEASURE]}
|
||||
measuresList={classMeasureList}
|
||||
onFieldChange={(measures: ISemantic.IMeasure[]) => {
|
||||
setExprTypeParamsState((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
[METRIC_DEFINE_TYPE.MEASURE]: {
|
||||
...prevState[METRIC_DEFINE_TYPE.MEASURE],
|
||||
measures,
|
||||
},
|
||||
};
|
||||
});
|
||||
}}
|
||||
onSqlChange={(expr: string) => {
|
||||
setExprTypeParamsState((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
[METRIC_DEFINE_TYPE.MEASURE]: {
|
||||
...prevState[METRIC_DEFINE_TYPE.MEASURE],
|
||||
expr,
|
||||
},
|
||||
};
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{defineType === METRIC_DEFINE_TYPE.METRIC && (
|
||||
<>
|
||||
<p className={styles.desc}>
|
||||
通过
|
||||
<Tag color="#2499ef14" className={styles.markerTag}>
|
||||
字段
|
||||
</Tag>
|
||||
和
|
||||
<Tag color="#2499ef14" className={styles.markerTag}>
|
||||
度量
|
||||
</Tag>
|
||||
创建的指标可用来创建新的指标
|
||||
</p>
|
||||
|
||||
<MetricMetricFormTable
|
||||
typeParams={exprTypeParamsState[METRIC_DEFINE_TYPE.METRIC]}
|
||||
metricList={createNewMetricList}
|
||||
onFieldChange={(metrics: ISemantic.IMetricTypeParamsItem[]) => {
|
||||
setExprTypeParamsState((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
[METRIC_DEFINE_TYPE.METRIC]: {
|
||||
...prevState[METRIC_DEFINE_TYPE.METRIC],
|
||||
metrics,
|
||||
},
|
||||
};
|
||||
});
|
||||
}}
|
||||
onSqlChange={(expr: string) => {
|
||||
setExprTypeParamsState((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
[METRIC_DEFINE_TYPE.METRIC]: {
|
||||
...prevState[METRIC_DEFINE_TYPE.METRIC],
|
||||
expr,
|
||||
},
|
||||
};
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{defineType === METRIC_DEFINE_TYPE.FIELD && (
|
||||
<>
|
||||
<MetricFieldFormTable
|
||||
typeParams={exprTypeParamsState[METRIC_DEFINE_TYPE.FIELD]}
|
||||
fieldList={fieldList}
|
||||
onFieldChange={(fields: ISemantic.IFieldTypeParamsItem[]) => {
|
||||
setExprTypeParamsState((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
[METRIC_DEFINE_TYPE.FIELD]: {
|
||||
...prevState[METRIC_DEFINE_TYPE.FIELD],
|
||||
fields,
|
||||
},
|
||||
};
|
||||
});
|
||||
}}
|
||||
onSqlChange={(expr: string) => {
|
||||
setExprTypeParamsState((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
[METRIC_DEFINE_TYPE.FIELD]: {
|
||||
...prevState[METRIC_DEFINE_TYPE.FIELD],
|
||||
expr,
|
||||
},
|
||||
};
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -456,9 +670,9 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
return (
|
||||
<Modal
|
||||
forceRender
|
||||
width={1300}
|
||||
width={800}
|
||||
style={{ top: 48 }}
|
||||
styles={{ padding: '32px 40px 48px' }}
|
||||
// styles={{ padding: '32px 40px 48px' }}
|
||||
destroyOnClose
|
||||
title={`${isEdit ? '编辑' : '新建'}指标`}
|
||||
maskClosable={false}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { Button, Input, Space } from 'antd';
|
||||
import { Button, Input, Space, Tag } from 'antd';
|
||||
import ProTable from '@ant-design/pro-table';
|
||||
import ProCard from '@ant-design/pro-card';
|
||||
import SqlEditor from '@/components/SqlEditor';
|
||||
import BindMeasuresTable from './BindMeasuresTable';
|
||||
import FormLabelRequire from './FormLabelRequire';
|
||||
import styles from './style.less';
|
||||
import { ISemantic } from '../data';
|
||||
|
||||
type Props = {
|
||||
datasourceId?: number;
|
||||
typeParams: ISemantic.ITypeParams;
|
||||
typeParams: ISemantic.IMeasureTypeParams;
|
||||
measuresList: ISemantic.IMeasure[];
|
||||
onFieldChange: (measures: ISemantic.IMeasure[]) => void;
|
||||
onSqlChange: (sql: string) => void;
|
||||
@@ -25,7 +26,6 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
||||
onSqlChange,
|
||||
}) => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const [measuresModalVisible, setMeasuresModalVisible] = useState<boolean>(false);
|
||||
const [measuresParams, setMeasuresParams] = useState(
|
||||
typeParams || {
|
||||
@@ -44,6 +44,7 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
||||
{
|
||||
dataIndex: 'bizName',
|
||||
title: '度量名称',
|
||||
tooltip: '由模型名称_字段名称拼接而来',
|
||||
},
|
||||
{
|
||||
dataIndex: 'constraint',
|
||||
@@ -74,6 +75,11 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'agg',
|
||||
title: '聚合函数',
|
||||
},
|
||||
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'x',
|
||||
@@ -105,7 +111,6 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
headerTitle={<FormLabelRequire title="度量列表" />}
|
||||
tooltip="基于本主题域下所有数据源的度量来创建指标,且该列表的度量为了加以区分,均已加上数据源名称作为前缀,选中度量后,可基于这几个度量来写表达式,若是选中的度量来自不同的数据源,系统将会自动join来计算该指标"
|
||||
rowKey="name"
|
||||
columns={columns}
|
||||
dataSource={measuresParams?.measures || []}
|
||||
@@ -127,8 +132,24 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
||||
/>
|
||||
<ProCard
|
||||
title={<FormLabelRequire title="度量表达式" />}
|
||||
tooltip="度量表达式由上面选择的度量组成,如选择了度量A和B,则可将表达式写成A+B"
|
||||
// tooltip="由于度量已自带聚合函数,因此通过度量创建指标时,表达式中无需再写聚合函数,如
|
||||
// 通过度量a和度量b来创建指标,由于建模的时候度量a和度量b被指定了聚合函数SUM,因此创建指标时表达式只需要写成 a+b, 而不需要带聚合函数"
|
||||
>
|
||||
<p
|
||||
className={styles.desc}
|
||||
style={{ border: 'unset', padding: 0, marginBottom: 20, marginLeft: 2 }}
|
||||
>
|
||||
在
|
||||
<Tag color="#2499ef14" className={styles.markerTag}>
|
||||
建模时
|
||||
</Tag>
|
||||
度量已指定了
|
||||
<Tag color="#2499ef14" className={styles.markerTag}>
|
||||
聚合函数
|
||||
</Tag>
|
||||
,在度量模式下,表达式无需再写聚合函数,如:
|
||||
通过指定了聚合函数SUM的度量a和度量b来创建指标,表达式只需要写成 a+b
|
||||
</p>
|
||||
<SqlEditor
|
||||
value={exprString}
|
||||
onChange={(sql: string) => {
|
||||
@@ -149,11 +170,12 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
||||
}
|
||||
selectedMeasuresList={measuresParams?.measures || []}
|
||||
onSubmit={async (values: any[]) => {
|
||||
const measures = values.map(({ bizName, name, expr, datasourceId }) => {
|
||||
const measures = values.map(({ bizName, name, expr, datasourceId, agg }) => {
|
||||
return {
|
||||
bizName,
|
||||
name,
|
||||
expr,
|
||||
agg,
|
||||
datasourceId,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -240,6 +240,9 @@
|
||||
.ant-pro-table-search-query-filter {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.ant-pro-query-filter {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.ant-pro-table-list-toolbar-container {
|
||||
padding-top: 0;
|
||||
}
|
||||
@@ -391,4 +394,26 @@
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin: 0;
|
||||
padding: 25px;
|
||||
color: #667085;
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-top: 1px solid #eee;
|
||||
margin-bottom: 10px;
|
||||
.markerTag {
|
||||
color: #2499ef;
|
||||
font-size: 14px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.textLink {
|
||||
color: #101828;
|
||||
&:hover {
|
||||
color: #69b1ff;
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export const IS_TAG_ENUM = {
|
||||
};
|
||||
|
||||
export const SENSITIVE_LEVEL_COLOR = {
|
||||
[SENSITIVE_LEVEL.LOW]: 'lime',
|
||||
[SENSITIVE_LEVEL.LOW]: 'geekblue',
|
||||
[SENSITIVE_LEVEL.MID]: 'warning',
|
||||
[SENSITIVE_LEVEL.HIGH]: 'error',
|
||||
};
|
||||
@@ -71,3 +71,9 @@ export const DatePeridMap = {
|
||||
sys_imp_week: DateRangeType.WEEK,
|
||||
sys_imp_month: DateRangeType.MONTH,
|
||||
};
|
||||
|
||||
export enum METRIC_DEFINE_TYPE {
|
||||
FIELD = 'FIELD',
|
||||
MEASURE = 'MEASURE',
|
||||
METRIC = 'METRIC',
|
||||
}
|
||||
|
||||
@@ -14,6 +14,16 @@ export type SensitiveLevel = 0 | 1 | 2 | null;
|
||||
export type ToolBarSearchCallBack = (text: string) => void;
|
||||
|
||||
export declare namespace IDataSource {
|
||||
interface IExecuteSqlColumn {
|
||||
name?: string;
|
||||
type: string;
|
||||
nameEn: string;
|
||||
showType?: string;
|
||||
authorized?: boolean;
|
||||
dataFormatType?: string;
|
||||
dataFormat?: string;
|
||||
}
|
||||
|
||||
interface IIdentifiersItem {
|
||||
name: string;
|
||||
type: string;
|
||||
@@ -42,12 +52,17 @@ export declare namespace IDataSource {
|
||||
nameCh: string;
|
||||
isCreateMetric: number;
|
||||
}
|
||||
|
||||
interface IDataSourceDetailFieldsItem {
|
||||
dataType: string;
|
||||
fieldName: string;
|
||||
}
|
||||
interface IDataSourceDetail {
|
||||
queryType: string;
|
||||
sqlQuery: string;
|
||||
tableQuery: string;
|
||||
identifiers: IIdentifiersItem[];
|
||||
|
||||
fields: IDataSourceDetailFieldsItem[];
|
||||
dimensions: IDimensionsItem[];
|
||||
measures: IMeasuresItem[];
|
||||
}
|
||||
@@ -168,11 +183,30 @@ export declare namespace ISemantic {
|
||||
isCreateMetric?: number;
|
||||
datasourceId: number;
|
||||
}
|
||||
interface ITypeParams {
|
||||
|
||||
interface IFieldTypeParamsItem {
|
||||
fieldName: string;
|
||||
}
|
||||
|
||||
interface IMetricTypeParamsItem {
|
||||
id: number;
|
||||
bizName: string;
|
||||
}
|
||||
|
||||
interface IMeasureTypeParams {
|
||||
measures: IMeasure[];
|
||||
expr: string;
|
||||
}
|
||||
|
||||
interface IMetricTypeParams {
|
||||
expr: string;
|
||||
metrics: IMetricTypeParamsItem[];
|
||||
}
|
||||
interface IFieldTypeParams {
|
||||
expr: string;
|
||||
fields: IFieldTypeParamsItem[];
|
||||
}
|
||||
|
||||
interface IDrillDownDimensionItem {
|
||||
dimensionId: number;
|
||||
necessary?: boolean;
|
||||
@@ -201,7 +235,10 @@ export declare namespace ISemantic {
|
||||
hasAdminRes: boolean;
|
||||
type: string;
|
||||
tags: string[];
|
||||
typeParams: ITypeParams;
|
||||
// typeParams: IMeasureTypeParams;
|
||||
metricDefineByMeasureParams: IMeasureTypeParams;
|
||||
metricDefineByFieldParams: IFieldTypeParams;
|
||||
metricDefineByMetricParams: IMetricTypeParams;
|
||||
fullPath: string;
|
||||
dataFormatType: string;
|
||||
dataFormat: string;
|
||||
|
||||
@@ -125,14 +125,14 @@ export function queryMetric(data: any): Promise<any> {
|
||||
return request.post(`${process.env.API_BASE_URL}metric/queryMetric`, queryParams);
|
||||
}
|
||||
|
||||
export function creatExprMetric(data: any): Promise<any> {
|
||||
return request.post(`${process.env.API_BASE_URL}metric/creatExprMetric`, {
|
||||
export function createMetric(data: any): Promise<any> {
|
||||
return request.post(`${process.env.API_BASE_URL}metric/createMetric`, {
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateExprMetric(data: any): Promise<any> {
|
||||
return request.post(`${process.env.API_BASE_URL}metric/updateExprMetric`, {
|
||||
export function updateMetric(data: any): Promise<any> {
|
||||
return request.post(`${process.env.API_BASE_URL}metric/updateMetric`, {
|
||||
data,
|
||||
});
|
||||
}
|
||||
@@ -430,6 +430,12 @@ export function getModelDetail(data: any): Promise<any> {
|
||||
return request.get(`${process.env.API_BASE_URL}model/getModel/${data.modelId}`);
|
||||
}
|
||||
|
||||
export function getMetricsToCreateNewMetric(data: any): Promise<any> {
|
||||
return request.get(
|
||||
`${process.env.API_BASE_URL}metric/getMetricsToCreateNewMetric/${data.modelId}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function createDictTask(data: any): Promise<any> {
|
||||
return request(`${process.env.CHAT_API_BASE_URL}dict/task`, {
|
||||
method: 'POST',
|
||||
@@ -546,3 +552,11 @@ export function metricStarState(data: { id: number; state: boolean }): Promise<a
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function getDatabaseParameters(): Promise<any> {
|
||||
return request.get(`${process.env.API_BASE_URL}database/getDatabaseParameters`);
|
||||
}
|
||||
|
||||
export function getDatabaseDetail(id: number): Promise<any> {
|
||||
return request.get(`${process.env.API_BASE_URL}database/${id}`);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { API } from '@/services/API';
|
||||
import { ISemantic } from './data';
|
||||
import type { DataNode } from 'antd/lib/tree';
|
||||
import { Form, Input, InputNumber, Switch, Select } from 'antd';
|
||||
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||
import { ConfigParametersItem } from '../System/types';
|
||||
const FormItem = Form.Item;
|
||||
const { TextArea } = Input;
|
||||
|
||||
export const changeTreeData = (treeData: API.DomainList, auth?: boolean): DataNode[] => {
|
||||
return treeData.map((item: any) => {
|
||||
@@ -125,3 +130,68 @@ export const findLeafNodesFromDomainList = (
|
||||
|
||||
return leafNodes;
|
||||
};
|
||||
|
||||
export const genneratorFormItemList = (itemList: ConfigParametersItem[]) => {
|
||||
return itemList.map((item) => {
|
||||
const { dataType, name, comment, placeholder, description, require } = item;
|
||||
let defaultItem = <Input />;
|
||||
switch (dataType) {
|
||||
case 'string':
|
||||
if (name === 'password') {
|
||||
defaultItem = <Input.Password placeholder={placeholder} />;
|
||||
} else {
|
||||
defaultItem = <Input placeholder={placeholder} />;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'longText':
|
||||
defaultItem = <TextArea placeholder={placeholder} style={{ height: 100 }} />;
|
||||
break;
|
||||
case 'number':
|
||||
defaultItem = <InputNumber placeholder={placeholder} style={{ width: '100%' }} />;
|
||||
break;
|
||||
case 'bool':
|
||||
return (
|
||||
<FormItem
|
||||
name={name}
|
||||
label={comment}
|
||||
key={name}
|
||||
valuePropName="checked"
|
||||
getValueFromEvent={(value) => {
|
||||
return value === true ? 'true' : 'false';
|
||||
}}
|
||||
getValueProps={(value) => {
|
||||
return {
|
||||
checked: value === 'true',
|
||||
};
|
||||
}}
|
||||
>
|
||||
<Switch />
|
||||
</FormItem>
|
||||
);
|
||||
case 'list': {
|
||||
const { candidateValues = [] } = item;
|
||||
const options = candidateValues.map((value) => {
|
||||
return { label: value, value };
|
||||
});
|
||||
defaultItem = (
|
||||
<Select style={{ width: '100%' }} options={options} placeholder={placeholder} />
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
defaultItem = <Input placeholder={placeholder} />;
|
||||
break;
|
||||
}
|
||||
return (
|
||||
<FormItem
|
||||
name={name}
|
||||
key={name}
|
||||
rules={[{ required: !!require, message: `请输入${comment}` }]}
|
||||
label={<FormItemTitle title={comment} subTitle={description} />}
|
||||
>
|
||||
{defaultItem}
|
||||
</FormItem>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -17,21 +17,19 @@ import React, { useState, useEffect } from 'react';
|
||||
import { getSystemConfig, saveSystemConfig } from '@/services/user';
|
||||
import ProCard from '@ant-design/pro-card';
|
||||
import SelectTMEPerson from '@/components/SelectTMEPerson';
|
||||
import { SystemConfigParametersItem, SystemConfig } from './types';
|
||||
import { ConfigParametersItem, SystemConfig } from './types';
|
||||
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||
import { groupBy } from 'lodash';
|
||||
import { genneratorFormItemList } from '../SemanticModel/utils';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
const { TextArea } = Input;
|
||||
const System: React.FC = () => {
|
||||
const [systemConfig, setSystemConfig] = useState<Record<string, SystemConfigParametersItem[]>>(
|
||||
{},
|
||||
);
|
||||
const [systemConfig, setSystemConfig] = useState<Record<string, ConfigParametersItem[]>>({});
|
||||
const [anchorItems, setAnchorItems] = useState<{ key: string; href: string; title: string }[]>(
|
||||
[],
|
||||
);
|
||||
const [configSource, setConfigSource] = useState<SystemConfig>();
|
||||
const [paramDescMap, setParamDescMap] = useState<Record<string, string>>({});
|
||||
|
||||
useEffect(() => {
|
||||
querySystemConfig();
|
||||
@@ -52,25 +50,13 @@ const System: React.FC = () => {
|
||||
setAnchorItems(anchor);
|
||||
setSystemConfig(groupByConfig);
|
||||
setInitData(admins, parameters);
|
||||
initDescMap(parameters);
|
||||
setConfigSource(data);
|
||||
} else {
|
||||
message.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
const initDescMap = (systemConfigParameters: SystemConfigParametersItem[]) => {
|
||||
const descData = systemConfigParameters.reduce(
|
||||
(descMap: Record<string, string>, item: SystemConfigParametersItem) => {
|
||||
descMap[item.name] = item.description;
|
||||
return descMap;
|
||||
},
|
||||
{},
|
||||
);
|
||||
setParamDescMap(descData);
|
||||
};
|
||||
|
||||
const setInitData = (admins: string[], systemConfigParameters: SystemConfigParametersItem[]) => {
|
||||
const setInitData = (admins: string[], systemConfigParameters: ConfigParametersItem[]) => {
|
||||
const fieldsValue = systemConfigParameters.reduce(
|
||||
(fields, item) => {
|
||||
const { name, value } = item;
|
||||
@@ -95,7 +81,6 @@ const System: React.FC = () => {
|
||||
return {
|
||||
...item,
|
||||
value: submitData[name],
|
||||
description: paramDescMap[name],
|
||||
};
|
||||
}
|
||||
return item;
|
||||
@@ -143,69 +128,7 @@ const System: React.FC = () => {
|
||||
bordered
|
||||
id={key}
|
||||
>
|
||||
{itemList.map((item) => {
|
||||
const { dataType, name, comment } = item;
|
||||
let defaultItem = <Input />;
|
||||
switch (dataType) {
|
||||
case 'string':
|
||||
defaultItem = <TextArea placeholder="" style={{ height: 100 }} />;
|
||||
break;
|
||||
case 'number':
|
||||
defaultItem = <InputNumber style={{ width: '100%' }} />;
|
||||
break;
|
||||
case 'bool':
|
||||
return (
|
||||
<FormItem
|
||||
name={name}
|
||||
label={comment}
|
||||
key={name}
|
||||
valuePropName="checked"
|
||||
getValueFromEvent={(value) => {
|
||||
return value === true ? 'true' : 'false';
|
||||
}}
|
||||
getValueProps={(value) => {
|
||||
return {
|
||||
checked: value === 'true',
|
||||
};
|
||||
}}
|
||||
>
|
||||
<Switch />
|
||||
</FormItem>
|
||||
);
|
||||
case 'list': {
|
||||
const { candidateValues = [] } = item;
|
||||
const options = candidateValues.map((value) => {
|
||||
return { label: value, value };
|
||||
});
|
||||
defaultItem = <Select style={{ width: '100%' }} options={options} />;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
defaultItem = <Input />;
|
||||
break;
|
||||
}
|
||||
return (
|
||||
<FormItem
|
||||
name={name}
|
||||
key={name}
|
||||
label={
|
||||
<FormItemTitle
|
||||
title={comment}
|
||||
subTitle={paramDescMap[name]}
|
||||
// subTitleEditable={true}
|
||||
onSubTitleChange={(title) => {
|
||||
setParamDescMap({
|
||||
...paramDescMap,
|
||||
[name]: title,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{defaultItem}
|
||||
</FormItem>
|
||||
);
|
||||
})}
|
||||
{genneratorFormItemList(itemList)}
|
||||
</ProCard>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
export type SystemConfigParametersItem = {
|
||||
export type ConfigParametersItem = {
|
||||
dataType: string;
|
||||
name: string;
|
||||
comment: string;
|
||||
value: string;
|
||||
candidateValues: string[];
|
||||
description: string;
|
||||
require?: boolean;
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export type SystemConfig = {
|
||||
id: number;
|
||||
admin: string;
|
||||
admins: string[];
|
||||
parameters: SystemConfigParametersItem[];
|
||||
parameters: ConfigParametersItem[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user