diff --git a/webapp/packages/supersonic-fe/src/components/MDatePicker/DynamicDate.tsx b/webapp/packages/supersonic-fe/src/components/MDatePicker/DynamicDate.tsx index 917eb0526..663e48b34 100644 --- a/webapp/packages/supersonic-fe/src/components/MDatePicker/DynamicDate.tsx +++ b/webapp/packages/supersonic-fe/src/components/MDatePicker/DynamicDate.tsx @@ -1,15 +1,13 @@ -import React, { useRef, forwardRef, useImperativeHandle, useState, useEffect } from 'react'; -import type { Ref, ReactNode } from 'react'; +import React, { forwardRef, useImperativeHandle, useState, useEffect } from 'react'; +import type { Ref } from 'react'; import type { RadioChangeEvent } from 'antd'; import { QuestionCircleOutlined } from '@ant-design/icons'; import { objToList } from '@/utils/utils'; -// import { LatestDateMap } from '@/services/global/type'; import { DateRangeType, DateRangeTypeToPickerMap, DateRangePicker } from './type'; import { SHORT_CUT_ITEM_LIST, datePeriodTypeWordingMap, getDynamicDateRangeStringByParams, - getDateStrings, datePeriodTypeMap, dateRangeTypeExchangeDatePeriodTypeMap, } from './utils'; @@ -74,7 +72,6 @@ const DynamicDate: React.FC = forwardRef( ) => { useImperativeHandle(ref, () => ({ dynamicDateUpdateAdvancedPanelFormData: () => { - // return [...dataSource]; if (advancedConfigType) { updateAdvancedPanelFormData( advancedPanelFormData[advancedConfigType], @@ -420,7 +417,6 @@ const DynamicDate: React.FC = forwardRef( }} /> - {nodeDataSource?.sourceData?.name} - {nodeDataSource?.targetData?.name} + {nodeDataSource?.sourceData?.name} + {nodeDataSource?.targetData?.name} - + {nodeModel?.sourceData?.name} - + {nodeModel?.targetData?.name} = ({ width={400} destroyOnClose getContainer={false} - title={'数据源关联信息'} + title={'模型关联信息'} mask={false} open={open} footer={renderFooter()} diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/DefaultSettingForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/DefaultSettingForm.tsx new file mode 100644 index 000000000..e6c1c2196 --- /dev/null +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/DefaultSettingForm.tsx @@ -0,0 +1,220 @@ +import { useEffect, useState, forwardRef, useImperativeHandle } from 'react'; +import type { ForwardRefRenderFunction } from 'react'; +import FormItemTitle from '@/components/FormHelper/FormItemTitle'; + +import { Form, Input, Select, InputNumber } from 'antd'; + +import { wrapperTransTypeAndId } from '../../utils'; + +import { ISemantic } from '../../data'; +import { ChatConfigType, TransType, SemanticNodeType } from '../../enum'; +import TransTypeTag from '../../components/TransTypeTag'; + +type Props = { + // entityData: any; + // chatConfigKey: string; + chatConfigType: ChatConfigType.TAG | ChatConfigType.METRIC; + metricList?: ISemantic.IMetricItem[]; + dimensionList?: ISemantic.IDimensionItem[]; + form: any; + // domainId: number; + // onSubmit: (params?: any) => void; +}; + +const FormItem = Form.Item; +const Option = Select.Option; + +const formDefaultValue = { + unit: 7, + period: 'DAY', + timeMode: 'LAST', +}; + +const DefaultSettingForm: ForwardRefRenderFunction = ( + { metricList, dimensionList, chatConfigType, form }, + ref, +) => { + const [dataItemListOptions, setDataItemListOptions] = useState([]); + + const initData = () => { + form.setFieldsValue({ + queryConfig: { + [defaultConfigKeyMap[chatConfigType]]: { + timeDefaultConfig: { + ...formDefaultValue, + }, + }, + }, + }); + }; + + useEffect(() => { + if (form && !form.getFieldValue('id')) { + initData(); + } + }, []); + + const defaultConfigKeyMap = { + [ChatConfigType.TAG]: 'tagTypeDefaultConfig', + [ChatConfigType.METRIC]: 'metricTypeDefaultConfig', + }; + + useEffect(() => { + if (Array.isArray(dimensionList) && Array.isArray(metricList)) { + const dimensionEnum = dimensionList.map((item: ISemantic.IDimensionItem) => { + const { name, id, bizName } = item; + return { + name, + label: ( + <> + + {name} + + ), + value: wrapperTransTypeAndId(TransType.DIMENSION, id), + bizName, + id, + transType: TransType.DIMENSION, + }; + }); + const metricEnum = metricList.map((item: ISemantic.IMetricItem) => { + const { name, id, bizName } = item; + return { + name, + label: ( + <> + + {name} + + ), + value: wrapperTransTypeAndId(TransType.METRIC, id), + bizName, + id, + transType: TransType.METRIC, + }; + }); + setDataItemListOptions([...dimensionEnum, ...metricEnum]); + } + }, [dimensionList, metricList]); + + return ( + <> + {chatConfigType === ChatConfigType.TAG && ( + { + const result: { dimensionIds: number[]; metricIds: number[] } = { + dimensionIds: [], + metricIds: [], + }; + items.forEach((item: any) => { + if (item.transType === TransType.DIMENSION) { + result.dimensionIds.push(item.id); + } + if (item.transType === TransType.METRIC) { + result.metricIds.push(item.id); + } + }); + return result; + }} + getValueProps={(value) => { + const { dimensionIds, metricIds } = value || {}; + const dimensionValues = Array.isArray(dimensionIds) + ? dimensionIds.map((id: number) => { + return wrapperTransTypeAndId(TransType.DIMENSION, id); + }) + : []; + const metricValues = Array.isArray(metricIds) + ? metricIds.map((id: number) => { + return wrapperTransTypeAndId(TransType.METRIC, id); + }) + : []; + return { + value: [...dimensionValues, ...metricValues], + }; + }} + > + + + + + + + )} + + + + + + + + + + ); +}; + +export default forwardRef(DefaultSettingForm); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/DimensionMetricTransferModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/DimensionMetricTransferModal.tsx index ac60fc74e..beafe9c79 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/DimensionMetricTransferModal.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/DimensionMetricTransferModal.tsx @@ -4,19 +4,21 @@ import { ISemantic } from '../../data'; import { TransType } from '../../enum'; import DimensionMetricVisibleTransfer from '../../components/Entity/DimensionMetricVisibleTransfer'; -import { wrapperTransTypeAndId } from '../../components/Entity/utils'; +import { wrapperTransTypeAndId } from '../../utils'; export type ModelCreateFormModalProps = { - dimensionList: ISemantic.IDimensionItem[]; - metricList: ISemantic.IMetricItem[]; + dimensionList?: ISemantic.IDimensionItem[]; + metricList?: ISemantic.IMetricItem[]; modelId?: number; selectedTransferKeys: React.Key[]; + toolbarSolt?: ReactNode; onCancel: () => void; onSubmit: (values: any, selectedKeys: React.Key[]) => void; }; const DimensionMetricTransferModal: React.FC = ({ modelId, + toolbarSolt, selectedTransferKeys, metricList, dimensionList, @@ -36,6 +38,9 @@ const DimensionMetricTransferModal: React.FC = ({ }; useEffect(() => { + if (!dimensionList || !metricList) { + return; + } const sourceDimensionList = dimensionList.reduce((mergeList: any[], item) => { mergeList.push(addItemKey(item, TransType.DIMENSION)); return mergeList; @@ -84,7 +89,7 @@ const DimensionMetricTransferModal: React.FC = ({ return ( {toolbarSolt}, '已加入维度/指标']} listStyle={{ width: 520, height: 600, @@ -94,26 +99,27 @@ const DimensionMetricTransferModal: React.FC = ({ onChange={(newTargetKeys: string[]) => { const removeDimensionList: ISemantic.IDimensionItem[] = []; const removeMetricList: ISemantic.IMetricItem[] = []; - const dimensionItemChangeList = dimensionList.reduce( - (dimensionChangeList: any[], item: any) => { - if (newTargetKeys.includes(wrapperTransTypeAndId(TransType.DIMENSION, item.id))) { - dimensionChangeList.push(item); - } else { - removeDimensionList.push(item.id); - } - return dimensionChangeList; - }, - [], - ); + const dimensionItemChangeList = Array.isArray(dimensionList) + ? dimensionList.reduce((dimensionChangeList: any[], item: any) => { + if (newTargetKeys.includes(wrapperTransTypeAndId(TransType.DIMENSION, item.id))) { + dimensionChangeList.push(item); + } else { + removeDimensionList.push(item.id); + } + return dimensionChangeList; + }, []) + : []; - const metricItemChangeList = metricList.reduce((metricChangeList: any[], item: any) => { - if (newTargetKeys.includes(wrapperTransTypeAndId(TransType.METRIC, item.id))) { - metricChangeList.push(item); - } else { - removeMetricList.push(item.id); - } - return metricChangeList; - }, []); + const metricItemChangeList = Array.isArray(metricList) + ? metricList.reduce((metricChangeList: any[], item: any) => { + if (newTargetKeys.includes(wrapperTransTypeAndId(TransType.METRIC, item.id))) { + metricChangeList.push(item); + } else { + removeMetricList.push(item.id); + } + return metricChangeList; + }, []) + : []; setSelectedItemList([...dimensionItemChangeList, ...metricItemChangeList]); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/MetricInfoCreateForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/MetricInfoCreateForm.tsx deleted file mode 100644 index f877242ee..000000000 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/MetricInfoCreateForm.tsx +++ /dev/null @@ -1,773 +0,0 @@ -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 } from '../constant'; -import { formLayout } from '@/components/FormHelper/utils'; -import FormItemTitle from '@/components/FormHelper/FormItemTitle'; -import styles from './style.less'; -import { getMetricsToCreateNewMetric, getModelDetail, getDrillDownDimension } 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 'umi'; - -export type CreateFormProps = { - datasourceId?: number; - domainId: number; - modelId: number; - createModalVisible: boolean; - metricItem: any; - 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 = ({ - 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([]); - - 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([]); - - const [defineType, setDefineType] = useState(METRIC_DEFINE_TYPE.MEASURE); - - const [createNewMetricList, setCreateNewMetricList] = useState([]); - const [fieldList, setFieldList] = useState([]); - const [isPercentState, setIsPercentState] = useState(false); - const [isDecimalState, setIsDecimalState] = useState(false); - const [hasMeasuresState, setHasMeasuresState] = useState(true); - const [llmLoading, setLlmLoading] = useState(false); - - const [tagOptions, setTagOptions] = useState<{ label: string; value: string }[]>([]); - - const [metricRelationModalOpenState, setMetricRelationModalOpenState] = useState(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 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) { - 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, - dataFormat, - dataFormatType, - alias, - tags, - metricDefineType, - metricDefineByMeasureParams, - metricDefineByMetricParams, - metricDefineByFieldParams, - } = metricItem; - const isPercent = dataFormatType === 'percent'; - const isDecimal = dataFormatType === 'decimal'; - const initValue = { - id, - name, - bizName, - sensitiveLevel, - description, - tags, - // 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 } = await saveMetricQuery(queryParams); - if (code === 200) { - message.success('编辑指标成功'); - onSubmit?.(queryParams); - 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) { - // form.setFieldValue('alias', Array.from(new Set([...formAlias, ...data]))); - 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) { - setCreateNewMetricList(data); - } else { - message.error('获取指标标签失败'); - } - }; - - const renderContent = () => { - if (currentStep === 1) { - return ( -
-
- { - setDefineType(e.target.value); - }} - > - 按度量 - 按指标 - 按字段 - -
- {defineType === METRIC_DEFINE_TYPE.MEASURE && ( - <> - { - 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 && ( - <> -

- 通过 - - 字段 - - 和 - - 度量 - - 创建的指标可用来创建新的指标 -

- - { - 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 && ( - <> - { - 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, - }, - }; - }); - }} - /> - - )} -
- ); - } - - return ( - <> - - - - - - - - - - - - - - - - - -

- 在录入指标时,请务必详细填写指标口径。口径描述对于理解指标的含义、计算方法和使用场景至关重要。一个清晰、准确的口径描述可以帮助其他用户更好地理解和使用该指标,避免因为误解而导致错误的数据分析和决策。在填写口径时,建议包括以下信息: -

-

1. 指标的计算方法:详细说明指标是如何计算的,包括涉及的公式、计算步骤等。

-

2. 数据来源:描述指标所依赖的数据来源,包括数据表、字段等信息。

-

3. 使用场景:说明该指标适用于哪些业务场景,以及如何在这些场景中使用该指标。

-

4. 任何其他相关信息:例如数据更新频率、数据质量要求等。

-

- 请确保口径描述清晰、简洁且易于理解,以便其他用户能够快速掌握指标的核心要点。 -

- - } - /> - } - rules={[{ required: true, message: '请输入业务口径' }]} - > -