mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-15 06:27:21 +00:00
(feature)(webapp) add filter modify and similar questions (#213)
This commit is contained in:
@@ -1,20 +1,27 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { AGG_TYPE_MAP, PREFIX_CLS } from '../../common/constants';
|
||||
import { ChatContextType, FilterItemType } from '../../common/type';
|
||||
import { ChatContextType, DateInfoType, EntityInfoType, FilterItemType } from '../../common/type';
|
||||
import { DatePicker } from 'antd';
|
||||
import { CheckCircleFilled } from '@ant-design/icons';
|
||||
import classNames from 'classnames';
|
||||
import SwicthEntity from './SwitchEntity';
|
||||
import Loading from './Loading';
|
||||
import FilterItem from './FilterItem';
|
||||
import moment from 'moment';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
type Props = {
|
||||
parseLoading: boolean;
|
||||
parseInfoOptions: ChatContextType[];
|
||||
parseTip: string;
|
||||
currentParseInfo?: ChatContextType;
|
||||
agentId?: number;
|
||||
dimensionFilters: FilterItemType[];
|
||||
dateInfo: DateInfoType;
|
||||
entityInfo: EntityInfoType;
|
||||
onSelectParseInfo: (parseInfo: ChatContextType) => void;
|
||||
onSwitchEntity: (entityId: string) => void;
|
||||
onFiltersChange: (filters: FilterItemType[]) => void;
|
||||
onDateInfoChange: (dateRange: any) => void;
|
||||
};
|
||||
|
||||
const MAX_OPTION_VALUES_COUNT = 2;
|
||||
@@ -24,16 +31,18 @@ const ParseTip: React.FC<Props> = ({
|
||||
parseInfoOptions,
|
||||
parseTip,
|
||||
currentParseInfo,
|
||||
agentId,
|
||||
dimensionFilters,
|
||||
dateInfo,
|
||||
entityInfo,
|
||||
onSelectParseInfo,
|
||||
onSwitchEntity,
|
||||
onFiltersChange,
|
||||
onDateInfoChange,
|
||||
}) => {
|
||||
const prefixCls = `${PREFIX_CLS}-item`;
|
||||
|
||||
const getNode = (tipTitle: ReactNode, tipNode?: ReactNode, parseSucceed?: boolean) => {
|
||||
const contentContainerClass = classNames(`${prefixCls}-content-container`, {
|
||||
[`${prefixCls}-content-container-succeed`]: parseSucceed,
|
||||
});
|
||||
return (
|
||||
<div className={`${prefixCls}-parse-tip`}>
|
||||
<div className={`${prefixCls}-title-bar`}>
|
||||
@@ -43,7 +52,7 @@ const ParseTip: React.FC<Props> = ({
|
||||
{!tipNode && <Loading />}
|
||||
</div>
|
||||
</div>
|
||||
{tipNode && <div className={contentContainerClass}>{tipNode}</div>}
|
||||
{tipNode && <div className={`${prefixCls}-content-container`}>{tipNode}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -60,22 +69,38 @@ const ParseTip: React.FC<Props> = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const getTipNode = (parseInfo: ChatContextType, isOptions?: boolean, index?: number) => {
|
||||
const {
|
||||
modelName,
|
||||
dateInfo,
|
||||
dimensionFilters,
|
||||
dimensions,
|
||||
metrics,
|
||||
aggType,
|
||||
queryMode,
|
||||
properties,
|
||||
entity,
|
||||
elementMatches,
|
||||
nativeQuery,
|
||||
} = parseInfo || {};
|
||||
const {
|
||||
modelId,
|
||||
modelName,
|
||||
dimensions,
|
||||
metrics,
|
||||
aggType,
|
||||
queryMode,
|
||||
properties,
|
||||
entity,
|
||||
// entityInfo,
|
||||
elementMatches,
|
||||
nativeQuery,
|
||||
} = currentParseInfo || {};
|
||||
|
||||
const { startDate, endDate } = dateInfo || {};
|
||||
const { type } = properties || {};
|
||||
const entityAlias = entity?.alias?.[0]?.split('.')?.[0];
|
||||
const entityName = elementMatches?.find(item => item.element?.type === 'ID')?.element.name;
|
||||
|
||||
const entityDimensions = entityInfo?.dimensions?.filter(
|
||||
item =>
|
||||
!['zyqk_song_id', 'song_name', 'singer_id', 'zyqk_cmpny_id'].includes(item.bizName) &&
|
||||
!(
|
||||
entityInfo?.dimensions?.some(dimension => dimension.bizName === 'singer_id') &&
|
||||
item.bizName === 'singer_name'
|
||||
) &&
|
||||
!(
|
||||
entityInfo?.dimensions?.some(dimension => dimension.bizName === 'zyqk_cmpny_id') &&
|
||||
item.bizName === 'cmpny_name'
|
||||
)
|
||||
);
|
||||
|
||||
const getTipNode = () => {
|
||||
const dimensionItems = dimensions?.filter(item => item.type === 'DIMENSION');
|
||||
const metric = metrics?.[0];
|
||||
|
||||
@@ -90,15 +115,7 @@ const ParseTip: React.FC<Props> = ({
|
||||
queryMode === 'ENTITY_DETAIL' ? dimensionItems?.concat(metrics || []) : dimensionItems;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${prefixCls}-tip-content`}
|
||||
onClick={() => {
|
||||
if (isOptions && currentParseInfo === undefined) {
|
||||
onSelectParseInfo(parseInfo);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{index !== undefined && <div>{index + 1}.</div>}
|
||||
<div className={`${prefixCls}-tip-content`}>
|
||||
{!!agentType && queryMode !== 'DSL' ? (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
将由{agentType === 'plugin' ? '插件' : '内置'}工具
|
||||
@@ -112,37 +129,23 @@ const ParseTip: React.FC<Props> = ({
|
||||
!!entityName ? (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
<div className={`${prefixCls}-tip-item-name`}>{entityAlias}:</div>
|
||||
{!isOptions && (entityAlias === '歌曲' || entityAlias === '艺人') ? (
|
||||
<SwicthEntity
|
||||
entityName={entityName}
|
||||
chatContext={parseInfo}
|
||||
onSwitchEntity={onSwitchEntity}
|
||||
/>
|
||||
) : (
|
||||
<div className={itemValueClass}>{entityName}</div>
|
||||
)}
|
||||
<div className={itemValueClass}>{entityName}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
<div className={`${prefixCls}-tip-item-name`}>数据来源:</div>
|
||||
<div className={`${prefixCls}-tip-item-name`}>数据模型:</div>
|
||||
<div className={itemValueClass}>{modelName}</div>
|
||||
</div>
|
||||
)}
|
||||
{!queryMode?.includes('ENTITY') && metric && (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
<div className={`${prefixCls}-tip-item-name`}>指标:</div>
|
||||
<div className={itemValueClass}>{metric.name}</div>
|
||||
</div>
|
||||
)}
|
||||
{!isOptions && (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
<div className={`${prefixCls}-tip-item-name`}>数据时间:</div>
|
||||
<div className={itemValueClass}>
|
||||
{startDate === endDate ? startDate : `${startDate} ~ ${endDate}`}
|
||||
{!queryMode?.includes('ENTITY') &&
|
||||
metric &&
|
||||
!dimensions?.some(item => item.bizName?.includes('_id')) && (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
<div className={`${prefixCls}-tip-item-name`}>指标:</div>
|
||||
<div className={itemValueClass}>{metric.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{['METRIC_GROUPBY', 'METRIC_ORDERBY', 'ENTITY_DETAIL', 'DSL'].includes(queryMode) &&
|
||||
)}
|
||||
{['METRIC_GROUPBY', 'METRIC_ORDERBY', 'ENTITY_DETAIL', 'DSL'].includes(queryMode!) &&
|
||||
fields &&
|
||||
fields.length > 0 && (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
@@ -166,7 +169,10 @@ const ParseTip: React.FC<Props> = ({
|
||||
</div>
|
||||
)}
|
||||
{queryMode !== 'ENTITY_ID' &&
|
||||
entityDimensions &&
|
||||
entityDimensions?.length > 0 &&
|
||||
!dimensions?.some(item => item.bizName?.includes('_id')) &&
|
||||
entityDimensions.some(dimension => dimension.value != null) &&
|
||||
entityDimensions.map(dimension => (
|
||||
<div className={`${prefixCls}-tip-item`} key={dimension.itemId}>
|
||||
<div className={`${prefixCls}-tip-item-name`}>{dimension.name}:</div>
|
||||
@@ -185,49 +191,38 @@ const ParseTip: React.FC<Props> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const parseInfo = parseInfoOptions[0] || {};
|
||||
const { modelId, properties, entity, entityInfo, elementMatches, queryMode, dimensionFilters } =
|
||||
parseInfo || {};
|
||||
|
||||
const { type } = properties || {};
|
||||
const entityAlias = entity?.alias?.[0]?.split('.')?.[0];
|
||||
const entityName = elementMatches?.find(item => item.element?.type === 'ID')?.element.name;
|
||||
|
||||
const entityDimensions = entityInfo?.dimensions?.filter(
|
||||
item =>
|
||||
!['zyqk_song_id', 'song_name', 'singer_id', 'zyqk_cmpny_id'].includes(item.bizName) &&
|
||||
!(
|
||||
entityInfo?.dimensions?.some(dimension => dimension.bizName === 'singer_id') &&
|
||||
item.bizName === 'singer_name'
|
||||
) &&
|
||||
!(
|
||||
entityInfo?.dimensions?.some(dimension => dimension.bizName === 'zyqk_cmpny_id') &&
|
||||
item.bizName === 'cmpny_name'
|
||||
)
|
||||
);
|
||||
|
||||
const getFilterContent = (filters: any) => {
|
||||
const itemValueClass = `${prefixCls}-tip-item-value`;
|
||||
const { startDate, endDate } = dateInfo || {};
|
||||
return (
|
||||
<div className={`${prefixCls}-tip-item-filter-content`}>
|
||||
{filters.map((filter: any) => (
|
||||
<div className={`${prefixCls}-tip-item-option`} key={filter.name}>
|
||||
<span>
|
||||
<span className={`${prefixCls}-tip-item-filter-name`}>{filter.name}</span>
|
||||
{filter.operator !== '=' && filter.operator !== 'IN' ? ` ${filter.operator} ` : ':'}
|
||||
<div className={`${prefixCls}-tip-item-option`}>
|
||||
<span className={`${prefixCls}-tip-item-filter-name`}>数据时间:</span>
|
||||
{dimensions?.some(item => item.bizName?.includes('_id')) ? (
|
||||
<span className={itemValueClass}>
|
||||
{startDate === endDate ? startDate : `${startDate} ~ ${endDate}`}
|
||||
</span>
|
||||
{/* {queryMode !== 'DSL' && !filter.bizName?.includes('_id') ? ( */}
|
||||
{!filter.bizName?.includes('_id') ? (
|
||||
<FilterItem
|
||||
modelId={modelId}
|
||||
filters={dimensionFilters}
|
||||
filter={filter}
|
||||
onFiltersChange={onFiltersChange}
|
||||
/>
|
||||
) : (
|
||||
<span className={itemValueClass}>{filter.value}</span>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<RangePicker
|
||||
className={`${prefixCls}-range-picker`}
|
||||
value={[moment(startDate), moment(endDate)]}
|
||||
onChange={onDateInfoChange}
|
||||
allowClear={false}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{filters?.map((filter: any) => (
|
||||
<FilterItem
|
||||
modelId={modelId!}
|
||||
filters={dimensionFilters}
|
||||
filter={filter}
|
||||
chatContext={currentParseInfo!}
|
||||
entityAlias={entityAlias}
|
||||
agentId={agentId}
|
||||
onFiltersChange={onFiltersChange}
|
||||
onSwitchEntity={onSwitchEntity}
|
||||
key={filter.name}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
@@ -244,27 +239,34 @@ const ParseTip: React.FC<Props> = ({
|
||||
|
||||
const tipNode = (
|
||||
<div className={`${prefixCls}-tip`}>
|
||||
{getTipNode(parseInfo)}
|
||||
{[
|
||||
'METRIC_FILTER',
|
||||
'METRIC_ENTITY',
|
||||
'ENTITY_DETAIL',
|
||||
'ENTITY_LIST_FILTER',
|
||||
'ENTITY_ID',
|
||||
'DSL',
|
||||
].includes(queryMode) &&
|
||||
dimensionFilters &&
|
||||
dimensionFilters?.length > 0 &&
|
||||
getFiltersNode()}
|
||||
{getTipNode()}
|
||||
{getFiltersNode()}
|
||||
{(!type || queryMode === 'DSL') && entityAlias && entityAlias !== '厂牌' && entityName && (
|
||||
<div className={`${prefixCls}-switch-entity-tip`}>
|
||||
(如未匹配到相关{entityAlias},可点击{entityAlias === '艺人' ? '歌手' : entityAlias}ID切换)
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return getNode(
|
||||
<div className={`${prefixCls}-title-bar`}>
|
||||
意图解析
|
||||
{(!type || queryMode === 'DSL') && entityAlias && entityAlias !== '厂牌' && entityName && (
|
||||
<div className={`${prefixCls}-switch-entity-tip`}>
|
||||
(如果未匹配到您查询的{entityAlias},可点击{entityAlias}名切换)
|
||||
<div>意图解析{parseInfoOptions?.length > 1 ? ':' : ''}</div>
|
||||
{parseInfoOptions?.length > 1 && (
|
||||
<div className={`${prefixCls}-content-options`}>
|
||||
{parseInfoOptions.map((parseInfo, index) => (
|
||||
<div
|
||||
className={`${prefixCls}-content-option ${
|
||||
parseInfo.id === currentParseInfo?.id ? `${prefixCls}-content-option-active` : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
onSelectParseInfo(parseInfo);
|
||||
}}
|
||||
key={parseInfo.id}
|
||||
>
|
||||
解析{index + 1}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>,
|
||||
|
||||
Reference in New Issue
Block a user