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 cd0c06692..51aae41d4 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 @@ -11,7 +11,7 @@ export type ModelCreateFormModalProps = { metricList?: ISemantic.IMetricItem[]; modelId?: number; selectedTransferKeys: React.Key[]; - toolbarSolt?: ReactNode; + toolbarSolt?: React.ReactNode; onCancel: () => void; onSubmit: (values: any, selectedKeys: React.Key[]) => void; }; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/TagTransferModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/TagTransferModal.tsx new file mode 100644 index 000000000..48a863350 --- /dev/null +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/TagTransferModal.tsx @@ -0,0 +1,114 @@ +import React, { useState, useEffect } from 'react'; + +import { ISemantic } from '../../data'; + +import { TransType } from '../../enum'; +import DimensionMetricVisibleTransfer from '../../components/Entity/DimensionMetricVisibleTransfer'; +import { wrapperTransTypeAndId } from '../../utils'; + +export type ModelCreateFormModalProps = { + tagList?: ISemantic.ITagItem[]; + modelId?: number; + selectedTransferKeys: React.Key[]; + toolbarSolt?: React.ReactNode; + onCancel: () => void; + onSubmit: (values: any, selectedKeys: React.Key[]) => void; +}; + +const TagTransferModal: React.FC = ({ + modelId, + toolbarSolt, + selectedTransferKeys, + tagList, + onSubmit, +}) => { + const [sourceList, setSourceList] = useState([]); + const [selectedItemList, setSelectedItemList] = useState([]); + + const addItemKey = (item: any, transType: TransType) => { + const { id } = item; + const key = wrapperTransTypeAndId(transType, id); + return { + ...item, + transType, + key, + }; + }; + + useEffect(() => { + if (!tagList) { + return; + } + const sourceTagList = tagList.reduce((mergeList: any[], item) => { + mergeList.push(addItemKey(item, TransType.TAG)); + return mergeList; + }, []); + + const hasTagList = selectedItemList.reduce((modelTagList: ISemantic.ITagItem[], item) => { + const hasItem = sourceTagList.find((dataListItem: ISemantic.ITagItem) => { + return dataListItem.id === item.id; + }); + if (!hasItem) { + modelTagList.push(addItemKey(item, TransType.TAG)); + } + return modelTagList; + }, []); + + setSourceList([...sourceTagList, ...hasTagList]); + }, [tagList]); + + return ( + {toolbarSolt}, '已加入标签']} + listStyle={{ + width: 520, + height: 600, + }} + targetList={selectedTransferKeys} + sourceList={sourceList} + onChange={(newTargetKeys: string[]) => { + const removeTagList: ISemantic.ITagItem[] = []; + const tagItemChangeList = Array.isArray(tagList) + ? tagList.reduce((tagChangeList: any[], item: any) => { + if (newTargetKeys.includes(wrapperTransTypeAndId(TransType.TAG, item.id))) { + tagChangeList.push(item); + } else { + removeTagList.push(item.id); + } + return tagChangeList; + }, []) + : []; + + setSelectedItemList([...tagItemChangeList]); + + // 如果不是当前选中model中的指标或者维度,则先从本地数据中删除,避免后续请求数据更新时产生视觉上的界面闪烁 + const preUpdateSourceData = sourceList.filter((item) => { + const { id } = item; + + if (modelId !== item.modelId && removeTagList.includes(id)) { + return false; + } + + return true; + }); + setSourceList([...preUpdateSourceData]); + const dataSetModelConfigs = [...tagItemChangeList].reduce((config, item) => { + const { modelId, id } = item; + if (config[modelId]) { + config[modelId].tagIds.push(id); + } else { + config[modelId] = { + id: modelId, + tagIds: [id], + }; + } + return config; + }, {}); + + onSubmit?.(dataSetModelConfigs, newTargetKeys); + }} + /> + ); +}; + +export default TagTransferModal; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewCreateFormModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewCreateFormModal.tsx index f08da042b..201571bd4 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewCreateFormModal.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewCreateFormModal.tsx @@ -1,9 +1,9 @@ import React, { useState, useEffect, useRef } from 'react'; -import { Form, Button, Modal, Input, Select, Steps, Tabs, Space } from 'antd'; +import { Form, Button, Modal, Input, Select, Steps, Radio, Space } from 'antd'; import styles from '../../components/style.less'; import { message } from 'antd'; import { formLayout } from '@/components/FormHelper/utils'; -import { createView, updateView, getDimensionList, queryMetric } from '../../service'; +import { createView, updateView, getDimensionList, queryMetric, getTagList } from '../../service'; import { ISemantic } from '../../data'; import { isString } from 'lodash'; import FormItemTitle from '@/components/FormHelper/FormItemTitle'; @@ -34,6 +34,8 @@ const ViewCreateFormModal: React.FC = ({ currentModel: modelList[0]?.id, }); + const [queryType, setQueryType] = useState('METRIC'); + const [saveLoading, setSaveLoading] = useState(false); const [modalWidth, setModalWidth] = useState(800); const [selectedModelItem, setSelectedModelItem] = useState( @@ -46,19 +48,22 @@ const ViewCreateFormModal: React.FC = ({ form.setFieldsValue({ ...viewItem, }); + setQueryType(viewItem?.queryType); }, [viewItem]); const [dimensionList, setDimensionList] = useState(); const [metricList, setMetricList] = useState(); + const [tagList, setTagList] = useState(); useEffect(() => { if (selectedModelItem?.id) { queryDimensionList(selectedModelItem.id); queryMetricList(selectedModelItem.id); + queryTagList(selectedModelItem.id); } }, [selectedModelItem]); - const queryDimensionList = async (modelId) => { + const queryDimensionList = async (modelId: number) => { const { code, data, msg } = await getDimensionList({ modelId }); if (code === 200 && Array.isArray(data?.list)) { setDimensionList(data.list); @@ -67,7 +72,7 @@ const ViewCreateFormModal: React.FC = ({ } }; - const queryMetricList = async (modelId) => { + const queryMetricList = async (modelId: number) => { const { code, data, msg } = await queryMetric({ modelId }); if (code === 200 && Array.isArray(data?.list)) { setMetricList(data.list); @@ -76,6 +81,20 @@ const ViewCreateFormModal: React.FC = ({ } }; + const queryTagList = async (modelId: number) => { + const { code, data, msg } = await getTagList({ + modelIds: [modelId], + }); + + const { list } = data || {}; + if (code === 200) { + setTagList(list); + } else { + message.error(msg); + setTagList([]); + } + }; + const handleConfirm = async () => { const fieldsValue = await form.validateFields(); const viewModelConfigsMap = configTableRef?.current.getViewModelConfigs() || {}; @@ -83,6 +102,7 @@ const ViewCreateFormModal: React.FC = ({ const queryData: ISemantic.IModelItem = { ...formVals, ...fieldsValue, + queryType, dataSetDetail: { dataSetModelConfigs: Object.values(viewModelConfigsMap), }, @@ -99,7 +119,7 @@ const ViewCreateFormModal: React.FC = ({ } }; - const stepWidth = { + const stepWidth: any = { '0': 800, '1': 1200, '2': 800, @@ -140,23 +160,6 @@ const ViewCreateFormModal: React.FC = ({ ); } - // if (currentStep === 2) { - // return ( - // <> - // - // - // - // ); - // } return ( <> @@ -179,27 +182,29 @@ const ViewCreateFormModal: React.FC = ({ return ( <>
- {/* - { @@ -216,6 +221,7 @@ const ViewCreateFormModal: React.FC = ({ } dimensionList={dimensionList} metricList={metricList} + tagList={tagList} modelItem={selectedModelItem} viewItem={viewItem} ref={configTableRef} diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewModelConfigTransfer.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewModelConfigTransfer.tsx index 07ab70945..56352c883 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewModelConfigTransfer.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewModelConfigTransfer.tsx @@ -2,27 +2,42 @@ import { message } from 'antd'; import React, { forwardRef, useImperativeHandle, useState, useEffect } from 'react'; import type { ReactNode, Ref } from 'react'; import DimensionMetricTransferModal from './DimensionMetricTransferModal'; +import TagTransferModal from './TagTransferModal'; import { TransType } from '../../enum'; -import { getDimensionList, queryMetric } from '../../service'; +import { getDimensionList, queryMetric, getTagList } from '../../service'; import { wrapperTransTypeAndId } from '../../utils'; import { ISemantic } from '../../data'; import { isArrayOfValues } from '@/utils/utils'; type Props = { + queryType?: string; viewItem: ISemantic.IViewItem; modelItem?: ISemantic.IModelItem; dimensionList?: ISemantic.IDimensionItem[]; metricList?: ISemantic.IMetricItem[]; + tagList?: ISemantic.ITagItem[]; toolbarSolt?: ReactNode; }; const ViewModelConfigTransfer: React.FC = forwardRef( - ({ viewItem, modelItem, dimensionList, metricList, toolbarSolt }: Props, ref: Ref) => { + ( + { + queryType = 'METRIC', + viewItem, + modelItem, + dimensionList, + metricList, + tagList, + toolbarSolt, + }: Props, + ref: Ref, + ) => { const [selectedTransferKeys, setSelectedTransferKeys] = useState([]); const [viewModelConfigsMap, setViewModelConfigsMap] = useState({}); const [mergeDimensionList, setDimensionList] = useState(); const [mergeMetricList, setMetricList] = useState(); + const [mergeTagList, setTagList] = useState(); useImperativeHandle(ref, () => ({ getViewModelConfigs: () => { @@ -30,6 +45,33 @@ const ViewModelConfigTransfer: React.FC = forwardRef( }, })); + const queryTagListByIds = async (ids: number[]) => { + if (!isArrayOfValues(ids)) { + setTagList(tagList); + return; + } + const { code, data, msg } = await getTagList({ ids }); + if (code === 200 && Array.isArray(data?.list)) { + const mergeList = data?.list.reduce( + (modelTagList: ISemantic.ITagItem[], item: ISemantic.ITagItem) => { + const hasItem = Array.isArray(tagList) + ? tagList.find((dataListItem: ISemantic.ITagItem) => { + return dataListItem.id === item.id; + }) + : []; + if (!hasItem) { + return [item, ...modelTagList]; + } + return modelTagList; + }, + tagList, + ); + setTagList(mergeList); + } else { + message.error(msg); + } + }; + const queryDimensionListByIds = async (ids: number[]) => { if (!isArrayOfValues(ids)) { setDimensionList(dimensionList); @@ -38,7 +80,7 @@ const ViewModelConfigTransfer: React.FC = forwardRef( const { code, data, msg } = await getDimensionList({ ids }); if (code === 200 && Array.isArray(data?.list)) { const mergeList = data?.list.reduce( - (modelDimensionList: ISemantic.IDimensionItem[], item) => { + (modelDimensionList: ISemantic.IDimensionItem[], item: ISemantic.IDimensionItem) => { const hasItem = Array.isArray(dimensionList) ? dimensionList.find((dataListItem: ISemantic.IDimensionItem) => { return dataListItem.id === item.id; @@ -64,17 +106,20 @@ const ViewModelConfigTransfer: React.FC = forwardRef( } const { code, data, msg } = await queryMetric({ ids }); if (code === 200 && Array.isArray(data?.list)) { - const mergeList = data.list.reduce((modelMetricList: ISemantic.IMetricItem[], item) => { - const hasItem = Array.isArray(metricList) - ? metricList.find((dataListItem: ISemantic.IMetricItem) => { - return dataListItem.id === item.id; - }) - : []; - if (!hasItem) { - return [item, ...modelMetricList]; - } - return modelMetricList; - }, metricList); + const mergeList = data.list.reduce( + (modelMetricList: ISemantic.IMetricItem[], item: ISemantic.IMetricItem) => { + const hasItem = Array.isArray(metricList) + ? metricList.find((dataListItem: ISemantic.IMetricItem) => { + return dataListItem.id === item.id; + }) + : []; + if (!hasItem) { + return [item, ...modelMetricList]; + } + return modelMetricList; + }, + metricList, + ); setMetricList(mergeList); } else { message.error(msg); @@ -83,35 +128,45 @@ const ViewModelConfigTransfer: React.FC = forwardRef( useEffect(() => { const dataSetModelConfigs = viewItem?.dataSetDetail?.dataSetModelConfigs; + if (Array.isArray(dataSetModelConfigs)) { const idList: number[] = []; const transferKeys: React.Key[] = []; - const viewConfigMap = {}; - const allMetrics: number[] = []; - const allDimensions: number[] = []; + const viewConfigMap: any = {}; dataSetModelConfigs.forEach((item: ISemantic.IViewModelConfigItem) => { - const { id, metrics, dimensions } = item; + const { id, metrics, dimensions, tagIds } = item; idList.push(id); - allMetrics.push(...metrics); - allDimensions.push(...dimensions); viewConfigMap[id] = { ...item }; - if (Array.isArray(metrics)) { - metrics.forEach((metricId: number) => { - transferKeys.push(wrapperTransTypeAndId(TransType.METRIC, metricId)); - }); + + if (queryType === 'METRIC') { + if (Array.isArray(metrics)) { + metrics.forEach((metricId: number) => { + transferKeys.push(wrapperTransTypeAndId(TransType.METRIC, metricId)); + }); + } + if (Array.isArray(dimensions)) { + dimensions.forEach((dimensionId: number) => { + transferKeys.push(wrapperTransTypeAndId(TransType.DIMENSION, dimensionId)); + }); + } } - if (Array.isArray(dimensions)) { - dimensions.forEach((dimensionId: number) => { - transferKeys.push(wrapperTransTypeAndId(TransType.DIMENSION, dimensionId)); - }); + if (queryType === 'TAG') { + if (Array.isArray(tagIds)) { + tagIds.forEach((tagId: number) => { + transferKeys.push(wrapperTransTypeAndId(TransType.TAG, tagId)); + }); + } } }); setSelectedTransferKeys(transferKeys); setViewModelConfigsMap(viewConfigMap); } - }, []); + }, [queryType]); useEffect(() => { + if (queryType !== 'METRIC') { + return; + } if (!dimensionList || !metricList) { return; } @@ -132,40 +187,92 @@ const ViewModelConfigTransfer: React.FC = forwardRef( setDimensionList(dimensionList); setMetricList(metricList); } - }, [modelItem, dimensionList, metricList]); + }, [queryType, modelItem, dimensionList, metricList]); + + useEffect(() => { + if (queryType !== 'TAG') { + return; + } + if (!tagList) { + return; + } + const dataSetModelConfigs = isArrayOfValues(Object.values(viewModelConfigsMap)) + ? (Object.values(viewModelConfigsMap) as ISemantic.IViewModelConfigItem[]) + : viewItem?.dataSetDetail?.dataSetModelConfigs; + if (isArrayOfValues(dataSetModelConfigs)) { + const allTags: number[] = []; + dataSetModelConfigs.forEach((item: ISemantic.IViewModelConfigItem) => { + const { tagIds } = item; + allTags.push(...tagIds); + }); + queryTagListByIds(allTags); + } else { + setTagList(tagList); + } + }, [queryType, modelItem, tagList]); return ( <> - , - selectedKeys: React.Key[], - ) => { - const dataSetModelConfigs = Object.values( - submitData, - ) as ISemantic.IViewModelConfigItem[]; +
+ , + selectedKeys: React.Key[], + ) => { + const dataSetModelConfigs = Object.values( + submitData, + ) as ISemantic.IViewModelConfigItem[]; - if (isArrayOfValues(dataSetModelConfigs)) { - const allMetrics: number[] = []; - const allDimensions: number[] = []; - dataSetModelConfigs.forEach((item: ISemantic.IViewModelConfigItem) => { - const { metrics, dimensions } = item; - allMetrics.push(...metrics); - allDimensions.push(...dimensions); - }); - queryDimensionListByIds(allDimensions); - queryMetricListByIds(allMetrics); - } - setViewModelConfigsMap(submitData); - setSelectedTransferKeys(selectedKeys); - }} - onCancel={() => {}} - /> + if (isArrayOfValues(dataSetModelConfigs)) { + const allMetrics: number[] = []; + const allDimensions: number[] = []; + dataSetModelConfigs.forEach((item: ISemantic.IViewModelConfigItem) => { + const { metrics, dimensions } = item; + allMetrics.push(...metrics); + allDimensions.push(...dimensions); + }); + queryDimensionListByIds(allDimensions); + queryMetricListByIds(allMetrics); + } + setViewModelConfigsMap(submitData); + setSelectedTransferKeys(selectedKeys); + }} + onCancel={() => {}} + /> +
+
+ , + selectedKeys: React.Key[], + ) => { + const dataSetModelConfigs = Object.values( + submitData, + ) as ISemantic.IViewModelConfigItem[]; + + if (isArrayOfValues(dataSetModelConfigs)) { + const allTags: number[] = []; + dataSetModelConfigs.forEach((item: ISemantic.IViewModelConfigItem) => { + const { tagIds } = item; + allTags.push(...tagIds); + }); + queryTagListByIds(allTags); + } + setViewModelConfigsMap(submitData); + setSelectedTransferKeys(selectedKeys); + }} + onCancel={() => {}} + /> +
); }, diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewSearchFormModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewSearchFormModal.tsx index 301e06311..53ae2963d 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewSearchFormModal.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/View/components/ViewSearchFormModal.tsx @@ -135,7 +135,7 @@ const ViewSearchFormModal: React.FC = ({ void; onChange?: (params?: any) => void; [key: string]: any; diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/TransTypeTag.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/TransTypeTag.tsx index 09698f695..633847fae 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/TransTypeTag.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/TransTypeTag.tsx @@ -16,6 +16,8 @@ const TransTypeTag: React.FC = ({ type }) => { {'指标'} ) : type === SemanticNodeType.DATASOURCE ? ( {'模型'} + ) : type === SemanticNodeType.TAG ? ( + {'标签'} ) : ( <> )} diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts index c88e06421..8c6c492e3 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts @@ -157,6 +157,7 @@ export declare namespace ISemantic { includesAll: boolean; metrics: number[]; dimensions: number[]; + tagIds: number[]; } interface IViewItem { diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts index 84392824a..9ec3a92d8 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts @@ -6,12 +6,14 @@ export enum ChatConfigType { export enum TransType { DIMENSION = 'DIMENSION', METRIC = 'METRIC', + TAG = 'TAG', } export enum SemanticNodeType { DATASOURCE = 'DATASOURCE', DIMENSION = 'DIMENSION', METRIC = 'METRIC', + TAG = 'TAG', } export enum MetricTypeWording {