mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-14 22:25:19 +00:00
[feature](weaapp) add agent
This commit is contained in:
@@ -4,6 +4,7 @@ import { PREFIX_CLS } from '../../../common/constants';
|
||||
type Props = {
|
||||
position: 'left' | 'right';
|
||||
width?: number | string;
|
||||
maxWidth?: number | string;
|
||||
height?: number | string;
|
||||
title?: string;
|
||||
followQuestions?: string[];
|
||||
@@ -17,6 +18,7 @@ type Props = {
|
||||
|
||||
const Message: React.FC<Props> = ({
|
||||
width,
|
||||
maxWidth,
|
||||
height,
|
||||
children,
|
||||
bubbleClassName,
|
||||
@@ -38,7 +40,7 @@ const Message: React.FC<Props> = ({
|
||||
<div className={`${prefixCls}-body`}>
|
||||
<div
|
||||
className={`${prefixCls}-bubble${bubbleClassName ? ` ${bubbleClassName}` : ''}`}
|
||||
style={{ width, height }}
|
||||
style={{ width, height, maxWidth }}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
|
||||
@@ -43,7 +43,11 @@ const MetricCard: React.FC<Props> = ({
|
||||
return (
|
||||
<div className={prefixCls}>
|
||||
<div className={`${prefixCls}-top-bar`}>
|
||||
<div className={`${prefixCls}-indicator-name`}>{indicatorColumn?.name}</div>
|
||||
{indicatorColumn?.name ? (
|
||||
<div className={`${prefixCls}-indicator-name`}>{indicatorColumn?.name}</div>
|
||||
) : (
|
||||
<div style={{ height: 32 }} />
|
||||
)}
|
||||
{(hasFilterSection || drillDownDimension) && (
|
||||
<div className={`${prefixCls}-filter-section-wrapper`}>
|
||||
(
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
|
||||
&-drill-down-dimensions {
|
||||
position: absolute;
|
||||
bottom: -38px;
|
||||
left: 0;
|
||||
bottom: -44px;
|
||||
left: -16;
|
||||
}
|
||||
}
|
||||
@@ -19,25 +19,19 @@ type Props = {
|
||||
};
|
||||
|
||||
const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApplyAuth }) => {
|
||||
const { queryColumns, queryResults, entityInfo, chatContext, queryMode, aggregateInfo } = data;
|
||||
|
||||
const { dateMode, unit } = chatContext?.dateInfo || {};
|
||||
|
||||
const { entityInfo, chatContext, queryMode } = data;
|
||||
const { dateInfo, dimensionFilters, elementMatches } = chatContext || {};
|
||||
const { dateMode, unit } = dateInfo || {};
|
||||
const dateOptions = DATE_TYPES[chatContext?.dateInfo?.period] || DATE_TYPES.DAY;
|
||||
const initialDateOption = dateOptions.find((option: any) => {
|
||||
return dateMode === 'RECENT' && option.value === unit;
|
||||
})?.value;
|
||||
|
||||
const [columns, setColumns] = useState<ColumnType[]>(queryColumns || []);
|
||||
const currentMetricField = columns.find((column: any) => column.showType === 'NUMBER');
|
||||
|
||||
const [activeMetricField, setActiveMetricField] = useState<FieldType>(chatContext.metrics?.[0]);
|
||||
const [dataSource, setDataSource] = useState<any[]>(queryResults);
|
||||
const [currentDateOption, setCurrentDateOption] = useState<number>(initialDateOption);
|
||||
const [dimensions, setDimensions] = useState<FieldType[]>(chatContext?.dimensions);
|
||||
const [columns, setColumns] = useState<ColumnType[]>([]);
|
||||
const [activeMetricField, setActiveMetricField] = useState<FieldType>();
|
||||
const [dataSource, setDataSource] = useState<any[]>([]);
|
||||
const [currentDateOption, setCurrentDateOption] = useState<number>();
|
||||
const [dimensions, setDimensions] = useState<FieldType[]>();
|
||||
const [drillDownDimension, setDrillDownDimension] = useState<DrillDownDimensionType>();
|
||||
const [aggregateInfoValue, setAggregateInfoValue] = useState<any>(aggregateInfo);
|
||||
const [dateModeValue, setDateModeValue] = useState(dateMode);
|
||||
const [aggregateInfoValue, setAggregateInfoValue] = useState<any>();
|
||||
const [dateModeValue, setDateModeValue] = useState<any>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const dateField: any = columns.find(
|
||||
@@ -47,9 +41,31 @@ const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApply
|
||||
const categoryColumnName =
|
||||
columns.find((column: any) => column.showType === 'CATEGORY')?.nameEn || '';
|
||||
|
||||
const entityId = dimensionFilters?.length > 0 ? dimensionFilters[0].value : undefined;
|
||||
const entityName = elementMatches?.find((item: any) => item.element?.type === 'ID')?.element
|
||||
?.name;
|
||||
|
||||
const isEntityMode =
|
||||
(queryMode === 'ENTITY_LIST_FILTER' || queryMode === 'METRIC_ENTITY') &&
|
||||
typeof entityId === 'string' &&
|
||||
entityName !== undefined;
|
||||
|
||||
useEffect(() => {
|
||||
const { queryColumns, queryResults, chatContext, aggregateInfo } = data;
|
||||
|
||||
const initialDateOption = dateOptions.find((option: any) => {
|
||||
return dateMode === 'RECENT' && option.value === unit;
|
||||
})?.value;
|
||||
|
||||
setColumns(queryColumns || []);
|
||||
setActiveMetricField(chatContext?.metrics?.[0]);
|
||||
setDataSource(queryResults);
|
||||
}, [queryResults]);
|
||||
setCurrentDateOption(initialDateOption);
|
||||
setDimensions(chatContext?.dimensions);
|
||||
setDrillDownDimension(undefined);
|
||||
setAggregateInfoValue(aggregateInfo);
|
||||
setDateModeValue(chatContext?.dateInfo?.dateMode);
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (queryMode === 'METRIC_GROUPBY') {
|
||||
@@ -117,14 +133,14 @@ const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApply
|
||||
});
|
||||
};
|
||||
|
||||
const currentMetricField = columns.find((column: any) => column.showType === 'NUMBER');
|
||||
|
||||
if (!currentMetricField) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const prefixCls = `${CLS_PREFIX}-metric-trend`;
|
||||
|
||||
const { dimensionFilters } = chatContext || {};
|
||||
|
||||
const hasFilterSection = dimensionFilters?.length > 0;
|
||||
|
||||
return (
|
||||
@@ -174,59 +190,61 @@ const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApply
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{aggregateInfoValue?.metricInfos?.length > 0 && (
|
||||
<MetricInfo aggregateInfo={aggregateInfoValue} />
|
||||
)}
|
||||
<div className={`${prefixCls}-date-options`}>
|
||||
{dateOptions.map((dateOption: { label: string; value: number }, index: number) => {
|
||||
const dateOptionClass = classNames(`${prefixCls}-date-option`, {
|
||||
[`${prefixCls}-date-active`]: dateOption.value === currentDateOption,
|
||||
[`${prefixCls}-date-mobile`]: isMobile,
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
key={dateOption.value}
|
||||
className={dateOptionClass}
|
||||
onClick={() => {
|
||||
selectDateOption(dateOption.value);
|
||||
}}
|
||||
>
|
||||
{dateOption.label}
|
||||
{dateOption.value === currentDateOption && (
|
||||
<div className={`${prefixCls}-active-identifier`} />
|
||||
)}
|
||||
</div>
|
||||
{index !== dateOptions.length - 1 && (
|
||||
<div className={`${prefixCls}-date-option-divider`} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Spin spinning={loading}>
|
||||
{dataSource?.length === 1 || chartIndex % 2 === 1 ? (
|
||||
<Table data={{ ...data, queryResults: dataSource }} onApplyAuth={onApplyAuth} />
|
||||
) : (
|
||||
<MetricTrendChart
|
||||
model={entityInfo?.modelInfo.name}
|
||||
dateColumnName={dateColumnName}
|
||||
categoryColumnName={categoryColumnName}
|
||||
metricField={currentMetricField}
|
||||
resultList={dataSource}
|
||||
triggerResize={triggerResize}
|
||||
onApplyAuth={onApplyAuth}
|
||||
<div className={`${prefixCls}-content`}>
|
||||
{aggregateInfoValue?.metricInfos?.length > 0 && (
|
||||
<MetricInfo aggregateInfo={aggregateInfoValue} />
|
||||
)}
|
||||
<div className={`${prefixCls}-date-options`}>
|
||||
{dateOptions.map((dateOption: { label: string; value: number }, index: number) => {
|
||||
const dateOptionClass = classNames(`${prefixCls}-date-option`, {
|
||||
[`${prefixCls}-date-active`]: dateOption.value === currentDateOption,
|
||||
[`${prefixCls}-date-mobile`]: isMobile,
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
key={dateOption.value}
|
||||
className={dateOptionClass}
|
||||
onClick={() => {
|
||||
selectDateOption(dateOption.value);
|
||||
}}
|
||||
>
|
||||
{dateOption.label}
|
||||
{dateOption.value === currentDateOption && (
|
||||
<div className={`${prefixCls}-active-identifier`} />
|
||||
)}
|
||||
</div>
|
||||
{index !== dateOptions.length - 1 && (
|
||||
<div className={`${prefixCls}-date-option-divider`} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{dataSource?.length === 1 || chartIndex % 2 === 1 ? (
|
||||
<Table data={{ ...data, queryResults: dataSource }} onApplyAuth={onApplyAuth} />
|
||||
) : (
|
||||
<MetricTrendChart
|
||||
model={entityInfo?.modelInfo.name}
|
||||
dateColumnName={dateColumnName}
|
||||
categoryColumnName={categoryColumnName}
|
||||
metricField={currentMetricField}
|
||||
resultList={dataSource}
|
||||
triggerResize={triggerResize}
|
||||
onApplyAuth={onApplyAuth}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{queryMode.includes('METRIC') && !isEntityMode && (
|
||||
<DrillDownDimensions
|
||||
modelId={chatContext.modelId}
|
||||
drillDownDimension={drillDownDimension}
|
||||
dimensionFilters={chatContext.dimensionFilters}
|
||||
onSelectDimension={onSelectDimension}
|
||||
/>
|
||||
)}
|
||||
</Spin>
|
||||
{queryMode.includes('METRIC') && (
|
||||
<DrillDownDimensions
|
||||
modelId={chatContext.modelId}
|
||||
drillDownDimension={drillDownDimension}
|
||||
dimensionFilters={chatContext.dimensionFilters}
|
||||
onSelectDimension={onSelectDimension}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -48,6 +48,13 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
row-gap: 12px;
|
||||
}
|
||||
|
||||
&-indicator {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -5,7 +5,7 @@ import MetricCard from './MetricCard';
|
||||
import MetricTrend from './MetricTrend';
|
||||
import Table from './Table';
|
||||
import { ColumnType, DrillDownDimensionType, MsgDataType } from '../../common/type';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { queryData } from '../../service';
|
||||
|
||||
type Props = {
|
||||
@@ -25,6 +25,11 @@ const ChatMsg: React.FC<Props> = ({ question, data, chartIndex, isMobileMode, tr
|
||||
const [drillDownDimension, setDrillDownDimension] = useState<DrillDownDimensionType>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setColumns(queryColumns);
|
||||
setDataSource(queryResults);
|
||||
}, [queryColumns, queryResults]);
|
||||
|
||||
if (!queryColumns || !queryResults) {
|
||||
return null;
|
||||
}
|
||||
@@ -35,14 +40,15 @@ const ChatMsg: React.FC<Props> = ({ question, data, chartIndex, isMobileMode, tr
|
||||
const metricFields = columns.filter(item => item.showType === 'NUMBER');
|
||||
|
||||
const isMetricCard =
|
||||
queryMode.includes('METRIC') &&
|
||||
(queryMode.includes('METRIC') ||
|
||||
(queryMode === 'DSL' && singleData && metricFields.length === 1 && columns.length === 1)) &&
|
||||
(singleData || chatContext?.dateInfo?.startDate === chatContext?.dateInfo?.endDate);
|
||||
|
||||
const isText =
|
||||
columns.length === 1 &&
|
||||
columns[0].showType === 'CATEGORY' &&
|
||||
!queryMode.includes('METRIC') &&
|
||||
!queryMode.includes('ENTITY') &&
|
||||
((!queryMode.includes('METRIC') && !queryMode.includes('ENTITY')) ||
|
||||
queryMode === 'METRIC_INTERPRET') &&
|
||||
singleData;
|
||||
|
||||
const onLoadData = async (value: any) => {
|
||||
@@ -68,9 +74,43 @@ const ChatMsg: React.FC<Props> = ({ question, data, chartIndex, isMobileMode, tr
|
||||
|
||||
const getMsgContent = () => {
|
||||
if (isText) {
|
||||
let text = dataSource[0][columns[0].nameEn];
|
||||
let htmlCode: string;
|
||||
const match = text.match(/```html([\s\S]*?)```/);
|
||||
htmlCode = match && match[1].trim();
|
||||
if (htmlCode) {
|
||||
text = text.replace(/```html([\s\S]*?)```/, '');
|
||||
}
|
||||
let scriptCode: string;
|
||||
let scriptSrc: string;
|
||||
if (htmlCode) {
|
||||
scriptSrc = htmlCode.match(/<script src="([\s\S]*?)"><\/script>/)?.[1] || '';
|
||||
scriptCode =
|
||||
htmlCode.match(/<script type="text\/javascript">([\s\S]*?)<\/script>/)?.[1] || '';
|
||||
if (scriptSrc) {
|
||||
const script = document.createElement('script');
|
||||
script.src = scriptSrc;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
if (scriptCode) {
|
||||
const script = document.createElement('script');
|
||||
script.innerHTML = scriptCode;
|
||||
setTimeout(() => {
|
||||
document.body.appendChild(script);
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div style={{ lineHeight: '24px', width: 'fit-content' }}>
|
||||
{dataSource[0][columns[0].nameEn]}
|
||||
<div
|
||||
style={{
|
||||
lineHeight: '24px',
|
||||
width: 'fit-content',
|
||||
maxWidth: '100%',
|
||||
overflowX: 'hidden',
|
||||
}}
|
||||
>
|
||||
{htmlCode ? <pre>{text}</pre> : text}
|
||||
{!!htmlCode && <div dangerouslySetInnerHTML={{ __html: htmlCode }} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -103,15 +143,18 @@ const ChatMsg: React.FC<Props> = ({ question, data, chartIndex, isMobileMode, tr
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Bar
|
||||
data={{ ...data, queryColumns: columns, queryResults: dataSource }}
|
||||
triggerResize={triggerResize}
|
||||
loading={loading}
|
||||
drillDownDimension={drillDownDimension}
|
||||
onSelectDimension={onSelectDimension}
|
||||
/>
|
||||
);
|
||||
if (categoryField?.length > 0 && metricFields?.length > 0) {
|
||||
return (
|
||||
<Bar
|
||||
data={{ ...data, queryColumns: columns, queryResults: dataSource }}
|
||||
triggerResize={triggerResize}
|
||||
loading={loading}
|
||||
drillDownDimension={drillDownDimension}
|
||||
onSelectDimension={onSelectDimension}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <Table data={{ ...data, queryColumns: columns, queryResults: dataSource }} />;
|
||||
};
|
||||
|
||||
let width = '100%';
|
||||
@@ -135,6 +178,7 @@ const ChatMsg: React.FC<Props> = ({ question, data, chartIndex, isMobileMode, tr
|
||||
title={question}
|
||||
isMobileMode={isMobileMode}
|
||||
width={width}
|
||||
maxWidth={isText && !isMobile ? '80%' : undefined}
|
||||
queryMode={queryMode}
|
||||
>
|
||||
{getMsgContent()}
|
||||
|
||||
Reference in New Issue
Block a user