Files
supersonic/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricInfoCreateForm.tsx
tristanliu f10cc90262 [improvement][headless-fe] remove modle column (#1034)
* [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.

* [improvement][semantic-fe] The organization structure selection feature has been added to the permission management.

* [improvement][semantic-fe] Improved user experience for the metric list.

* [improvement][semantic-fe] fix update the metric list.

* [improvement][headless-fe] Added view management functionality.

* [improvement][headless-fe] The view management functionality has been added. This feature allows users to create, edit, and manage different views within the system.

* [improvement][headless-fe] Added model editing side effect detection.

* [improvement][headless-fe] Fixed the logic error in view editing.

* [improvement][headless-fe] Fixed the issue with initializing dimension associations in metric settings.

* [improvement][headless-fe] Added the ability to hide the Q&A settings entry point.

* [improvement][headless-fe] Fixed the issue with selecting search results in metric field creation.

* [improvement][headless-fe] Added search functionality to the field list in model editing.

* [improvement][headless-fe] fix the field list in model editing

* [improvement][headless-fe] Restructured the data for the dimension value settings interface.

* [improvement][headless-fe] Added dynamic variable functionality to model creation based on SQL scripts.

* [improvement][headless-fe] Added support for passing dynamic variables as parameters in the executeSql function.

* [improvement][headless-fe] Resolved the issue where users were unable to select all options for dimensions, metrics, and fields in the metric generation process.

* [improvement][headless-fe] Replaced the term "view" with "dataset"

* [improvement][headless-fe] Added the ability to export metrics and dimensions to a specific target.

* [improvement][headless-fe] Enhanced dataset creation to support the tag mode.

* [improvement][headless-fe] Added tag value setting.

* [improvement][headless-fe] Optimized the tag setting system.

* [improvement][headless-fe] Optimized the tag setting system.

* [improvement][headless-fe] Updated the data initialization for model editing to use API requests instead.

* [improvement][headless-fe] Added search functionality to model management.

* [improvement][headless-fe] Removed field null validation during model editing.

* [improvement][headless-fe] Updated the batch operation button component.

* [improvement][headless-fe] Optimized the logic for initializing indicators in dimension value settings.

* [improvement][headless-fe] Adjusted the length of the input field for model editing names.

* [improvement][headless-fe]  Lock the version of the @ant-design/pro-table component and replace it with @ant-design/pro-components.

* [improvement][headless-fe] Optimized the style of the metrics market and tags market.

* [improvement][headless-fe] The quick creation of model fields now defaults to using the "comment" field for filling.

* [improvement][headless-fe] The quick creation of model fields now defaults to using the "comment" field for filling

* [improvement][headless-fe] The quick creation of model fields now defaults to using the "comment" field for filling.

* [improvement][headless-fe] Fixed the issue where the conditions for metric measurement creation were not being saved correctly.

* [improvement][headless-fe] Default value setting for hiding dimensions.

* [improvement][headless-fe] Updated the file imports in the project.

* [improvement][headless-fe] Adjusted the logic for displaying the tab in the theme domain.

* [improvement][headless-fe] Added term management functionality.

* [improvement][headless-fe] When creating a model, the current metric operator now allows for clearance.

* [improvement][headless-fe] Term management interface transformation

* [improvement][headless-fe] Migrating scaffold version to @umi/max

* [improvement][headless-fe] remove modle column
2024-05-27 16:38:05 +08:00

916 lines
28 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect, useRef, useState } from 'react';
import {
Form,
Button,
Modal,
Steps,
Input,
Select,
Radio,
Switch,
InputNumber,
message,
Result,
Row,
Col,
Space,
Tooltip,
Tag,
} from 'antd';
import { InfoCircleOutlined } from '@ant-design/icons';
import MetricMeasuresFormTable from './MetricMeasuresFormTable';
import { SENSITIVE_LEVEL_OPTIONS, METRIC_DEFINE_TYPE, TAG_DEFINE_TYPE } from '../constant';
import { formLayout } from '@/components/FormHelper/utils';
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
import styles from './style.less';
import {
getMetricsToCreateNewMetric,
getModelDetail,
getDrillDownDimension,
batchCreateTag,
batchDeleteTag,
} from '../service';
import MetricMetricFormTable from './MetricMetricFormTable';
import MetricFieldFormTable from './MetricFieldFormTable';
import DimensionAndMetricRelationModal from './DimensionAndMetricRelationModal';
import TableTitleTooltips from '../components/TableTitleTooltips';
import { createMetric, updateMetric, mockMetricAlias, getMetricTags } from '../service';
import { ISemantic } from '../data';
import { history } from '@umijs/max';
export type CreateFormProps = {
datasourceId?: number;
domainId?: number;
modelId?: number;
createModalVisible: boolean;
metricItem?: ISemantic.IMetricItem;
onCancel?: () => void;
onSubmit?: (values: any) => void;
};
const { Step } = Steps;
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,
modelId,
onCancel,
createModalVisible,
metricItem,
onSubmit,
}) => {
const isEdit = !!metricItem?.id;
const [currentStep, setCurrentStep] = useState(0);
const formValRef = useRef({} as any);
const [form] = Form.useForm();
const updateFormVal = (val: any) => {
const formVal = {
...formValRef.current,
...val,
};
formValRef.current = formVal;
};
const [classMeasureList, setClassMeasureList] = 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 [exprTypeParamsState, setExprTypeParamsState] = useState<ISemantic.IMeasure[]>([]);
const [defineType, setDefineType] = useState(METRIC_DEFINE_TYPE.MEASURE);
const [createNewMetricList, setCreateNewMetricList] = useState<ISemantic.IMetricItem[]>([]);
const [fieldList, setFieldList] = useState<ISemantic.IFieldTypeParamsItem[]>([]);
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 [tagOptions, setTagOptions] = useState<{ label: string; value: string }[]>([]);
const [metricRelationModalOpenState, setMetricRelationModalOpenState] = useState<boolean>(false);
const [drillDownDimensions, setDrillDownDimensions] = useState<
ISemantic.IDrillDownDimensionItem[]
>([]);
const [drillDownDimensionsConfig, setDrillDownDimensionsConfig] = useState<
ISemantic.IDrillDownDimensionItem[]
>([]);
const forward = () => setCurrentStep(currentStep + 1);
const backward = () => setCurrentStep(currentStep - 1);
const queryModelDetail = async () => {
const { code, data } = await getModelDetail({ modelId: modelId || metricItem?.modelId });
if (code === 200) {
if (Array.isArray(data?.modelDetail?.fields)) {
if (Array.isArray(metricItem?.metricDefineByFieldParams?.fields)) {
const fieldList = data.modelDetail.fields.map((item: ISemantic.IFieldTypeParamsItem) => {
const { fieldName } = item;
if (
metricItem?.metricDefineByFieldParams?.fields.find(
(measureParamsItem: ISemantic.IFieldTypeParamsItem) =>
measureParamsItem.fieldName === fieldName,
)
) {
return {
...item,
orderNumber: 9999,
};
}
return {
...item,
orderNumber: 0,
};
});
const sortList = fieldList.sort(
(
a: ISemantic.IFieldTypeParamsItem & { orderNumber: number },
b: ISemantic.IFieldTypeParamsItem & { orderNumber: number },
) => b.orderNumber - a.orderNumber,
);
setFieldList(sortList);
} else {
setFieldList(data.modelDetail.fields);
}
}
if (Array.isArray(data?.modelDetail?.measures)) {
if (Array.isArray(metricItem?.metricDefineByMeasureParams?.measures)) {
const measureList = data.modelDetail.measures.map((item: ISemantic.IMeasure) => {
const { bizName } = item;
if (
metricItem?.metricDefineByMeasureParams?.measures.find(
(measureParamsItem: ISemantic.IMeasure) => measureParamsItem.bizName === bizName,
)
) {
return {
...item,
orderNumber: 9999,
};
}
return {
...item,
orderNumber: 0,
};
});
const sortMeasureList = measureList.sort(
(
a: ISemantic.IMeasure & { orderNumber: number },
b: ISemantic.IMeasure & { orderNumber: number },
) => b.orderNumber - a.orderNumber,
);
setClassMeasureList(sortMeasureList);
} else {
setClassMeasureList(data.modelDetail.measures);
}
if (datasourceId) {
const hasMeasures = data.some(
(item: ISemantic.IMeasure) => item.datasourceId === datasourceId,
);
setHasMeasuresState(hasMeasures);
}
return;
}
}
setClassMeasureList([]);
};
const queryDrillDownDimension = async (metricId: number) => {
const { code, data, msg } = await getDrillDownDimension(metricId);
if (code === 200 && Array.isArray(data)) {
setDrillDownDimensionsConfig(data);
}
if (code !== 200) {
message.error(msg);
}
return [];
};
useEffect(() => {
queryModelDetail();
queryMetricsToCreateNewMetric();
queryMetricTags();
}, []);
const handleNext = async () => {
const fieldsValue = await form.validateFields();
const submitForm = {
...formValRef.current,
...fieldsValue,
metricDefineType: defineType,
[queryParamsTypeParamsKey[defineType]]: exprTypeParamsState[defineType],
};
updateFormVal(submitForm);
if (currentStep < 1) {
forward();
} else {
await saveMetric(submitForm);
}
};
const initData = () => {
const {
id,
name,
bizName,
description,
sensitiveLevel,
typeParams,
isTag,
dataFormat,
dataFormatType,
alias,
classifications,
metricDefineType,
metricDefineByMeasureParams,
metricDefineByMetricParams,
metricDefineByFieldParams,
} = metricItem;
const isPercent = dataFormatType === 'percent';
const isDecimal = dataFormatType === 'decimal';
const initValue = {
id,
name,
bizName,
sensitiveLevel,
description,
classifications,
isTag,
// isPercent,
dataFormatType: dataFormatType || '',
alias: alias && alias.trim() ? alias.split(',') : [],
dataFormat: dataFormat || {
decimalPlaces: 2,
needMultiply100: false,
},
};
const editInitFormVal = {
...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);
setDefineType(metricDefineType);
setIsPercentState(isPercent);
setIsDecimalState(isDecimal);
queryDrillDownDimension(metricItem?.id);
};
useEffect(() => {
if (isEdit) {
initData();
}
}, [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,
relateDimension: {
...(metricItem?.relateDimension || {}),
drillDownDimensions,
},
...fieldsValue,
};
const { alias, dataFormatType } = queryParams;
queryParams.alias = Array.isArray(alias) ? alias.join(',') : '';
if (!queryParams[queryParamsTypeParamsKey[defineType]]?.expr) {
message.error('请输入度量表达式');
return;
}
if (!dataFormatType) {
delete queryParams.dataFormat;
}
if (isEmptyConditions(defineType, queryParams[queryParamsTypeParamsKey[defineType]])) {
return;
}
let saveMetricQuery = createMetric;
if (queryParams.id) {
saveMetricQuery = updateMetric;
}
const { code, msg, data } = await saveMetricQuery(queryParams);
if (code === 200) {
if (queryParams.isTag) {
queryBatchExportTag(data.id || metricItem?.id);
}
if (metricItem?.id && !queryParams.isTag) {
queryBatchDelete(metricItem);
}
message.success('编辑指标成功');
onSubmit?.(queryParams);
return;
}
message.error(msg);
};
const queryBatchDelete = async (metricItem: ISemantic.IMetricItem) => {
const { code, msg } = await batchDeleteTag([
{
itemIds: [metricItem.id],
tagDefineType: TAG_DEFINE_TYPE.METRIC,
},
]);
if (code === 200) {
return;
}
message.error(msg);
};
const queryBatchExportTag = async (id: number) => {
const { code, msg } = await batchCreateTag([
{ itemId: id, tagDefineType: TAG_DEFINE_TYPE.METRIC },
]);
if (code === 200) {
return;
}
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 queryMetricTags = async () => {
const { code, data } = await getMetricTags();
if (code === 200) {
setTagOptions(
Array.isArray(data)
? data.map((tag: string) => {
return { label: tag, value: tag };
})
: [],
);
} else {
message.error('获取指标标签失败');
}
};
const queryMetricsToCreateNewMetric = async () => {
const { code, data } = await getMetricsToCreateNewMetric({
modelId: modelId || metricItem?.modelId,
});
if (code === 200) {
if (Array.isArray(metricItem?.metricDefineByMetricParams?.metrics)) {
const fieldList = data.map((item: ISemantic.IMetricTypeParamsItem) => {
const { bizName } = item;
if (
metricItem?.metricDefineByMetricParams?.metrics.find(
(measureParamsItem: ISemantic.IMetricTypeParamsItem) =>
measureParamsItem.bizName === bizName,
)
) {
return {
...item,
orderNumber: 9999,
};
}
return {
...item,
orderNumber: 0,
};
});
const sortList = fieldList.sort(
(
a: ISemantic.IMetricTypeParamsItem & { orderNumber: number },
b: ISemantic.IMetricTypeParamsItem & { orderNumber: number },
) => b.orderNumber - a.orderNumber,
);
setCreateNewMetricList(sortList);
} else {
setCreateNewMetricList(data);
}
} else {
message.error('获取指标标签失败');
}
};
const renderContent = () => {
if (currentStep === 1) {
return (
<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[]) => {
// setClassMeasureList(measures);
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>
</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>
);
}
return (
<>
<FormItem hidden={true} name="id" label="ID">
<Input placeholder="id" />
</FormItem>
<FormItem
name="name"
label="指标名称"
rules={[{ required: true, message: '请输入指标名称' }]}
>
<Input placeholder="名称不可重复" />
</FormItem>
<FormItem
name="bizName"
label="英文名称"
rules={[{ required: true, message: '请输入英文名称' }]}
>
<Input placeholder="名称不可重复" disabled={isEdit} />
</FormItem>
<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="classifications" label="分类">
<Select
mode="tags"
placeholder="输入分类名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
tokenSeparators={[',']}
maxTagCount={9}
options={tagOptions}
/>
</FormItem>
<FormItem
name="sensitiveLevel"
label="敏感度"
rules={[{ required: true, message: '请选择敏感度' }]}
>
<Select placeholder="请选择敏感度">
{SENSITIVE_LEVEL_OPTIONS.map((item) => (
<Option key={item.value} value={item.value}>
{item.label}
</Option>
))}
</Select>
</FormItem>
<FormItem
name="description"
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="请输入业务口径" />
</FormItem>
<Form.Item
label={
<FormItemTitle
title={`设为标签`}
subTitle={`如果勾选,代表取值都是一种'标签',可用作对实体的圈选`}
/>
}
name="isTag"
valuePropName="checked"
getValueFromEvent={(value) => {
return value === true ? 1 : 0;
}}
getValueProps={(value) => {
return {
checked: value === 1,
};
}}
>
<Switch />
</Form.Item>
<FormItem
label={
<FormItemTitle
title={'下钻维度配置'}
subTitle={'配置下钻维度后,将可以在指标卡中进行下钻'}
/>
}
>
<Button
type="primary"
onClick={() => {
setMetricRelationModalOpenState(true);
}}
>
</Button>
</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>
{(isPercentState || isDecimalState) && (
<FormItem
label={
<FormItemTitle
title={'小数位数'}
subTitle={`对小数位数进行设置如保留两位0.021252 -> 0.02${
isPercentState ? '%' : ''
}`}
/>
}
name={['dataFormat', 'decimalPlaces']}
>
<InputNumber placeholder="请输入需要保留小数位数" style={{ width: '300px' }} />
</FormItem>
)}
{isPercentState && (
<>
<FormItem
label={
<FormItemTitle
title={'原始值是否乘以100'}
subTitle={'如 原始值0.001 ->展示值0.1% '}
/>
}
name={['dataFormat', 'needMultiply100']}
valuePropName="checked"
>
<Switch />
</FormItem>
</>
)}
</>
);
};
const renderFooter = () => {
if (!hasMeasuresState) {
return <Button onClick={onCancel}></Button>;
}
if (currentStep === 1) {
return (
<>
<Button style={{ float: 'left' }} onClick={backward}>
</Button>
<Button onClick={onCancel}></Button>
<Button type="primary" onClick={handleNext}>
</Button>
</>
);
}
return (
<>
<Button onClick={onCancel}></Button>
<Button type="primary" onClick={handleNext}>
</Button>
</>
);
};
return (
<Modal
forceRender
width={800}
style={{ top: 48 }}
// styles={{ padding: '32px 40px 48px' }}
destroyOnClose
title={`${isEdit ? '编辑' : '新建'}指标`}
maskClosable={false}
open={createModalVisible}
footer={renderFooter()}
onCancel={onCancel}
>
{hasMeasuresState ? (
<>
<Steps style={{ marginBottom: 28 }} size="small" current={currentStep}>
<Step title="基本信息" />
<Step title="表达式" />
</Steps>
<Form
{...formLayout}
form={form}
initialValues={{
...formValRef.current,
dataFormatType: '',
}}
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}
>
{renderContent()}
</Form>
<DimensionAndMetricRelationModal
metricItem={metricItem}
relationsInitialValue={drillDownDimensionsConfig}
open={metricRelationModalOpenState}
onCancel={() => {
setMetricRelationModalOpenState(false);
}}
onSubmit={(relations) => {
setDrillDownDimensions(relations);
setMetricRelationModalOpenState(false);
}}
onRefreshRelationData={() => {
queryDrillDownDimension(metricItem?.id);
}}
/>
</>
) : (
<Result
status="warning"
subTitle="当前数据源缺少度量,无法创建指标。请前往数据源配置中,将字段设置为度量"
extra={
<Button
type="primary"
key="console"
onClick={() => {
history.replace(`/model/${domainId}/${modelId || metricItem?.modelId}/dataSource`);
onCancel?.();
}}
>
</Button>
}
/>
)}
</Modal>
);
};
export default MetricInfoCreateForm;