[feature](webapp) upgrade agent

This commit is contained in:
williamhliu
2023-09-04 11:46:36 +08:00
parent 559ef974b0
commit d5c5c63a75
50 changed files with 1334 additions and 1897 deletions

View File

@@ -62,6 +62,13 @@ export type ModelType = {
useCnt: number;
}
export type EntityDimensionType = {
bizName: string;
itemId: number;
name: string;
value: string;
}
export type ChatContextType = {
id: number;
queryId: number;
@@ -73,6 +80,7 @@ export type ChatContextType = {
dimensions: FieldType[];
metrics: FieldType[];
entity: { alias: string[], id: number };
entityInfo: { dimensions: EntityDimensionType[] };
elementMatches: any[];
queryMode: string;
dimensionFilters: FilterItemType[];
@@ -137,6 +145,7 @@ export type ParseDataType = {
}
export type QueryDataType = {
chatContext: ChatContextType;
aggregateInfo: AggregateInfoType;
queryColumns: ColumnType[];
queryResults: any[];

View File

@@ -7,29 +7,23 @@ import WebPage from '../ChatMsg/WebPage';
import Loading from './Loading';
type Props = {
question: string;
queryId?: number;
executeLoading: boolean;
entitySwitchLoading: boolean;
chartIndex: number;
executeTip?: string;
data?: MsgDataType;
isMobileMode?: boolean;
triggerResize?: boolean;
onChangeChart: () => void;
};
const ExecuteItem: React.FC<Props> = ({
question,
queryId,
executeLoading,
entitySwitchLoading,
chartIndex,
executeTip,
data,
isMobileMode,
triggerResize,
onChangeChart,
}) => {
const prefixCls = `${PREFIX_CLS}-item`;
@@ -71,13 +65,7 @@ const ExecuteItem: React.FC<Props> = ({
{data?.queryMode === 'WEB_PAGE' ? (
<WebPage id={queryId!} data={data} />
) : (
<ChatMsg
question={question}
data={data}
chartIndex={chartIndex}
isMobileMode={isMobileMode}
triggerResize={triggerResize}
/>
<ChatMsg data={data} chartIndex={chartIndex} triggerResize={triggerResize} />
)}
</Spin>
</div>

View File

@@ -0,0 +1,94 @@
import { Select, Spin } from 'antd';
import { PREFIX_CLS } from '../../common/constants';
import { FilterItemType } from '../../common/type';
import { useEffect, useMemo, useRef, useState } from 'react';
import { queryDimensionValues } from '../../service';
import debounce from 'lodash/debounce';
type Props = {
modelId: number;
filters: FilterItemType[];
filter: FilterItemType;
onFiltersChange: (filters: FilterItemType[]) => void;
};
const FilterItem: React.FC<Props> = ({ modelId, filters, filter, onFiltersChange }) => {
const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
const [loading, setLoading] = useState(false);
const fetchRef = useRef(0);
const prefixCls = `${PREFIX_CLS}-filter-item`;
const initData = async () => {
const { data } = await queryDimensionValues(modelId, filter.bizName, '');
setOptions(
data?.data?.resultList.map((item: any) => ({
label: item[filter.bizName],
value: item[filter.bizName],
})) || []
);
};
useEffect(() => {
if (typeof filter.value === 'string' && options.length === 0) {
initData();
}
}, []);
const debounceFetcher = useMemo(() => {
const loadOptions = (value: string) => {
fetchRef.current += 1;
const fetchId = fetchRef.current;
setOptions([]);
setLoading(true);
queryDimensionValues(modelId, filter.bizName, value).then(newOptions => {
if (fetchId !== fetchRef.current) {
return;
}
setOptions(
newOptions.data?.data?.resultList.map((item: any) => ({
label: item[filter.bizName],
value: item[filter.bizName],
})) || []
);
setLoading(false);
});
};
return debounce(loadOptions, 800);
}, [queryDimensionValues]);
const onChange = (value: string) => {
const newFilters = filters.map(item => {
if (item.bizName === filter.bizName) {
item.value = `${value}`;
}
return item;
});
onFiltersChange(newFilters);
};
return (
<span className={prefixCls}>
{typeof filter.value === 'string' ? (
<Select
bordered={false}
value={filter.value}
options={options}
className={`${prefixCls}-select-control`}
popupClassName={`${prefixCls}-select-popup`}
onSearch={debounceFetcher}
notFoundContent={loading ? <Spin size="small" /> : null}
onChange={onChange}
showSearch
/>
) : (
<span>{filter.value}</span>
)}
</span>
);
};
export default FilterItem;

View File

@@ -1,20 +1,20 @@
import React, { ReactNode } from 'react';
import { AGG_TYPE_MAP, PREFIX_CLS } from '../../common/constants';
import { ChatContextType } from '../../common/type';
import { ChatContextType, FilterItemType } from '../../common/type';
import { CheckCircleFilled, InfoCircleOutlined } from '@ant-design/icons';
import classNames from 'classnames';
import SwicthEntity from './SwitchEntity';
import { Tooltip } from 'antd';
import Loading from './Loading';
import FilterItem from './FilterItem';
type Props = {
parseLoading: boolean;
parseInfoOptions: ChatContextType[];
parseTip: string;
currentParseInfo?: ChatContextType;
optionMode?: boolean;
onSelectParseInfo: (parseInfo: ChatContextType) => void;
onSwitchEntity: (entityId: string) => void;
onFiltersChange: (filters: FilterItemType[]) => void;
};
const MAX_OPTION_VALUES_COUNT = 2;
@@ -24,9 +24,9 @@ const ParseTip: React.FC<Props> = ({
parseInfoOptions,
parseTip,
currentParseInfo,
optionMode,
onSelectParseInfo,
onSwitchEntity,
onFiltersChange,
}) => {
const prefixCls = `${PREFIX_CLS}-item`;
@@ -62,6 +62,7 @@ const ParseTip: React.FC<Props> = ({
const getTipNode = (parseInfo: ChatContextType, isOptions?: boolean, index?: number) => {
const {
modelId,
modelName,
dateInfo,
dimensionFilters,
@@ -73,27 +74,12 @@ const ParseTip: React.FC<Props> = ({
entity,
elementMatches,
} = parseInfo || {};
const { startDate, endDate } = dateInfo || {};
const dimensionItems = dimensions?.filter(item => item.type === 'DIMENSION');
const metric = metrics?.[0];
const tipContentClass = classNames(`${prefixCls}-tip-content`, {
[`${prefixCls}-tip-content-option`]: isOptions,
[`${prefixCls}-tip-content-option-active`]:
isOptions &&
currentParseInfo &&
JSON.stringify(currentParseInfo) === JSON.stringify(parseInfo),
[`${prefixCls}-tip-content-option-disabled`]:
isOptions &&
currentParseInfo !== undefined &&
JSON.stringify(currentParseInfo) !== JSON.stringify(parseInfo),
});
const itemValueClass = classNames({
[`${prefixCls}-tip-item-value`]: !isOptions,
[`${prefixCls}-tip-item-option`]: isOptions,
});
const itemValueClass = `${prefixCls}-tip-item-value`;
const entityId = dimensionFilters?.length > 0 ? dimensionFilters[0].value : undefined;
const entityAlias = entity?.alias?.[0]?.split('.')?.[0];
const entityName = elementMatches?.find(item => item.element?.type === 'ID')?.element.name;
@@ -106,14 +92,22 @@ const ParseTip: React.FC<Props> = ({
const getFilterContent = (filters: any) => {
return (
<div className={`${prefixCls}-tip-item-filter-content`}>
{filters.map((filter: any, index: number) => (
<div className={itemValueClass}>
{filters.map((filter: any) => (
<div className={`${prefixCls}-tip-item-option`}>
<span>
{filter.name}
<span className={`${prefixCls}-tip-item-filter-name`}>{filter.name}</span>
{filter.operator !== '=' ? ` ${filter.operator} ` : ''}
</span>
<span>{Array.isArray(filter.value) ? filter.value.join('') : filter.value}</span>
{index !== filters.length - 1 && <span></span>}
{queryMode !== 'DSL' && !filter.bizName?.includes('_id') ? (
<FilterItem
modelId={modelId}
filters={dimensionFilters}
filter={filter}
onFiltersChange={onFiltersChange}
/>
) : (
<span className={itemValueClass}>{filter.value}</span>
)}
</div>
))}
</div>
@@ -124,27 +118,16 @@ const ParseTip: React.FC<Props> = ({
return (
<div className={`${prefixCls}-tip-item`}>
<div className={`${prefixCls}-tip-item-name`}></div>
<Tooltip
title={
dimensionFilters.length > MAX_OPTION_VALUES_COUNT
? getFilterContent(dimensionFilters)
: ''
}
color="#fff"
overlayStyle={{ maxWidth: 'none' }}
>
<div className={`${prefixCls}-tip-item-content`}>
{getFilterContent(dimensionFilters.slice(0, MAX_OPTION_VALUES_COUNT))}
{dimensionFilters.length > MAX_OPTION_VALUES_COUNT && ' ...'}
</div>
</Tooltip>
<div className={`${prefixCls}-tip-item-content`}>
{getFilterContent(dimensionFilters)}
</div>
</div>
);
};
return (
<div
className={tipContentClass}
className={`${prefixCls}-tip-content`}
onClick={() => {
if (isOptions && currentParseInfo === undefined) {
onSelectParseInfo(parseInfo);
@@ -234,41 +217,45 @@ const ParseTip: React.FC<Props> = ({
);
};
let tipNode: ReactNode;
const parseInfo = parseInfoOptions[0] || {};
const { properties, entity, entityInfo, elementMatches, queryMode } = parseInfo || {};
if (parseInfoOptions.length > 1 || optionMode) {
tipNode = (
<div className={`${prefixCls}-multi-options`}>
<div>
<strong></strong>
</div>
<div className={`${prefixCls}-options`}>
{parseInfoOptions.map((item, index) => getTipNode(item, true, index))}
</div>
</div>
);
} else {
const { type } = parseInfoOptions[0]?.properties || {};
const entityAlias = parseInfoOptions[0]?.entity?.alias?.[0]?.split('.')?.[0];
const entityName = parseInfoOptions[0]?.elementMatches?.find(
item => item.element?.type === 'ID'
)?.element.name;
const queryMode = parseInfoOptions[0]?.queryMode;
const { type } = properties || {};
const entityAlias = entity?.alias?.[0]?.split('.')?.[0];
const entityName = elementMatches?.find(item => item.element?.type === 'ID')?.element.name;
tipNode = (
<div className={`${prefixCls}-tip`}>
{getTipNode(parseInfoOptions[0])}
{(!type || queryMode === 'DSL') && entityAlias && entityName && (
<div className={`${prefixCls}-switch-entity-tip`}>
<InfoCircleOutlined />
<div>
{entityAlias}{entityAlias}
const entityDimensions = entityInfo?.dimensions?.filter(
item =>
!['zyqk_song_id', 'song_name', 'singer_id'].includes(item.bizName) &&
!(
entityInfo?.dimensions?.some(dimension => dimension.bizName === 'singer_id') &&
item.bizName === 'singer_name'
)
);
const tipNode = (
<div className={`${prefixCls}-tip`}>
{getTipNode(parseInfo)}
{queryMode !== 'ENTITY_ID' && entityDimensions?.length > 0 && (
<div className={`${prefixCls}-entity-info`}>
{entityDimensions.map(dimension => (
<div className={`${prefixCls}-dimension-item`} key={dimension.itemId}>
<div className={`${prefixCls}-dimension-name`}>{dimension.name}</div>
<div className={`${prefixCls}-dimension-value`}>{dimension.value}</div>
</div>
))}
</div>
)}
{(!type || queryMode === 'DSL') && entityAlias && entityName && (
<div className={`${prefixCls}-switch-entity-tip`}>
<InfoCircleOutlined />
<div>
{entityAlias}{entityAlias}
</div>
)}
</div>
);
}
</div>
)}
</div>
);
return getNode('意图解析结果', tipNode, true);
};

View File

@@ -1,6 +1,6 @@
import { ChatContextType, MsgDataType, ParseStateEnum } from '../../common/type';
import { ChatContextType, FilterItemType, MsgDataType, ParseStateEnum } from '../../common/type';
import { useEffect, useState } from 'react';
import { chatExecute, chatParse, switchEntity } from '../../service';
import { chatExecute, chatParse, queryData, switchEntity } from '../../service';
import { PARSE_ERROR_TIP, PREFIX_CLS, SEARCH_EXCEPTION_TIP } from '../../common/constants';
import IconFont from '../IconFont';
import ParseTip from './ParseTip';
@@ -17,7 +17,6 @@ type Props = {
filter?: any[];
isLastMessage?: boolean;
msgData?: MsgDataType;
isMobileMode?: boolean;
isHistory?: boolean;
triggerResize?: boolean;
parseOptions?: ChatContextType[];
@@ -32,7 +31,6 @@ const ChatItem: React.FC<Props> = ({
agentId,
filter,
isLastMessage,
isMobileMode,
isHistory,
triggerResize,
msgData,
@@ -77,10 +75,7 @@ const ChatItem: React.FC<Props> = ({
return true;
};
const onExecute = async (
parseInfoValue: ChatContextType,
parseInfoOptions?: ChatContextType[]
) => {
const onExecute = async (parseInfoValue: ChatContextType) => {
setExecuteMode(true);
setExecuteLoading(true);
try {
@@ -88,24 +83,10 @@ const ChatItem: React.FC<Props> = ({
setExecuteLoading(false);
const valid = updateData(data);
if (onMsgDataLoaded) {
let parseOptions: ChatContextType[] = parseInfoOptions || [];
if (
parseInfoOptions &&
parseInfoOptions.length > 1 &&
(parseInfoOptions[0].queryMode.includes('METRIC') ||
parseInfoOptions[0].queryMode.includes('ENTITY'))
) {
parseOptions = parseInfoOptions.filter(
(item, index) =>
index === 0 ||
(!item.queryMode.includes('METRIC') && !item.queryMode.includes('ENTITY'))
);
}
onMsgDataLoaded(
{
...data.data,
chatContext: parseInfoValue,
parseOptions: parseOptions.length > 1 ? parseOptions.slice(1) : undefined,
},
valid
);
@@ -141,7 +122,7 @@ const ChatItem: React.FC<Props> = ({
setParseInfoOptions(parseInfos || []);
const parseInfoValue = parseInfos[0];
setParseInfo(parseInfoValue);
onExecute(parseInfoValue, parseInfos);
onExecute(parseInfoValue);
};
useEffect(() => {
@@ -167,8 +148,15 @@ const ChatItem: React.FC<Props> = ({
setParseInfoOptions([chatContext]);
};
const onChangeChart = () => {
setChartIndex(chartIndex + 1);
const onFiltersChange = async (dimensionFilters: FilterItemType[]) => {
setEntitySwitchLoading(true);
const chatContextValue = { ...(parseInfoOptions[0] || {}), dimensionFilters };
const res: any = await queryData(chatContextValue);
setEntitySwitchLoading(false);
const resChatContext = res.data?.data?.chatContext;
setData({ ...(res.data?.data || {}), chatContext: resChatContext || chatContextValue });
setParseInfo(resChatContext || chatContextValue);
setParseInfoOptions([resChatContext || chatContextValue]);
};
const onSelectParseInfo = async (parseInfoValue: ChatContextType) => {
@@ -197,22 +185,19 @@ const ChatItem: React.FC<Props> = ({
parseInfoOptions={parseOptions || parseInfoOptions.slice(0, 1)}
parseTip={parseTip}
currentParseInfo={parseInfo}
optionMode={parseOptions !== undefined}
onSelectParseInfo={onSelectParseInfo}
onSwitchEntity={onSwitchEntity}
onFiltersChange={onFiltersChange}
/>
{executeMode && (
<ExecuteItem
question={msg}
queryId={parseInfo?.queryId}
executeLoading={executeLoading}
entitySwitchLoading={entitySwitchLoading}
executeTip={executeTip}
chartIndex={chartIndex}
data={data}
isMobileMode={isMobileMode}
triggerResize={triggerResize}
onChangeChart={onChangeChart}
/>
)}
</div>

View File

@@ -1,6 +1,7 @@
@import '../../styles/index.less';
@chat-item-prefix-cls: ~'@{supersonic-chat-prefix}-item';
@filter-item-prefix-cls: ~'@{supersonic-chat-prefix}-filter-item';
.@{chat-item-prefix-cls} {
display: flex;
@@ -215,6 +216,12 @@
&-tip-item-filter-content {
display: flex;
align-items: center;
flex-wrap: wrap;
column-gap: 12px;
}
&-tip-item-filter-name {
color: var(--text-color-secondary);
}
&-mode-name {
@@ -231,6 +238,29 @@
font-weight: 500;
}
&-entity-info {
display: flex;
align-items: center;
column-gap: 12px;
margin-top: 4px;
color: var(--text-color-third);
font-size: 14px;
}
&-dimension-item {
display: flex;
align-items: center;
}
&-dimension-name {
color: var(--text-color-third);
}
&-dimension-value {
color: var(--chat-blue);
font-weight: 500;
}
&-metric-info-list {
margin-top: 30px;
display: flex;
@@ -263,3 +293,14 @@
user-select: text;
}
}
.@{filter-item-prefix-cls} {
&-select-control {
min-width: 120px;
}
.ant-select-selection-item {
color: var(--chat-blue);
font-weight: 500;
}
}

View File

@@ -1,5 +1,5 @@
import { PREFIX_CLS } from '../../../common/constants';
import { formatMetric, formatNumberWithCN } from '../../../utils/utils';
import { formatByDecimalPlaces, formatMetric, formatNumberWithCN } from '../../../utils/utils';
import ApplyAuth from '../ApplyAuth';
import { DrillDownDimensionType, MsgDataType } from '../../../common/type';
import PeriodCompareItem from './PeriodCompareItem';
@@ -27,12 +27,13 @@ const MetricCard: React.FC<Props> = ({
const { queryMode, queryColumns, queryResults, entityInfo, aggregateInfo, chatContext } = data;
const { metricInfos } = aggregateInfo || {};
const { dateInfo } = chatContext || {};
const { startDate } = dateInfo || {};
const indicatorColumn = queryColumns?.find(column => column.showType === 'NUMBER');
const indicatorColumnName = indicatorColumn?.nameEn || '';
const { dataFormatType, dataFormat } = indicatorColumn || {};
const value = queryResults?.[0]?.[indicatorColumnName] || 0;
const prefixCls = `${PREFIX_CLS}-metric-card`;
const matricCardClass = classNames(prefixCls, {
@@ -54,7 +55,7 @@ const MetricCard: React.FC<Props> = ({
{indicatorColumn?.name ? (
<div className={`${prefixCls}-indicator-name`}>{indicatorColumn?.name}</div>
) : (
<div style={{ height: 6 }} />
<div style={{ height: 10 }} />
)}
{drillDownDimension && (
<div className={`${prefixCls}-filter-section-wrapper`}>
@@ -73,19 +74,25 @@ const MetricCard: React.FC<Props> = ({
</div>
<Spin spinning={loading}>
<div className={indicatorClass}>
<div className={`${prefixCls}-date-range`}>{startDate}</div>
{indicatorColumn && !indicatorColumn?.authorized ? (
<ApplyAuth model={entityInfo?.modelInfo.name || ''} onApplyAuth={onApplyAuth} />
) : (
<div style={{ display: 'flex', alignItems: 'flex-end' }}>
<div className={`${prefixCls}-indicator-value`}>
{isNumber
? formatMetric(queryResults?.[0]?.[indicatorColumnName]) || '-'
: formatNumberWithCN(+queryResults?.[0]?.[indicatorColumnName])}
</div>
<div className={`${prefixCls}-indicator-switch`}>
<SwapOutlined onClick={handleNumberClick} />
{dataFormatType === 'percent' || dataFormatType === 'decimal'
? `${formatByDecimalPlaces(
dataFormat?.needMultiply100 ? +value * 100 : value,
dataFormat?.decimalPlaces || 2
)}${dataFormatType === 'percent' ? '%' : ''}`
: isNumber
? formatMetric(value) || '-'
: formatNumberWithCN(+value)}
</div>
{!isNaN(+value) && +value >= 10000 && (
<div className={`${prefixCls}-indicator-switch`}>
<SwapOutlined onClick={handleNumberClick} />
</div>
)}
</div>
)}
{metricInfos?.length > 0 && (
@@ -100,8 +107,8 @@ const MetricCard: React.FC<Props> = ({
{queryMode.includes('METRIC') && (
<div className={`${prefixCls}-drill-down-dimensions`}>
<DrillDownDimensions
modelId={chatContext.modelId}
dimensionFilters={chatContext.dimensionFilters}
modelId={chatContext?.modelId}
dimensionFilters={chatContext?.dimensionFilters}
drillDownDimension={drillDownDimension}
onSelectDimension={onSelectDimension}
/>

View File

@@ -4,7 +4,6 @@
.@{metric-card-prefix-cls} {
width: 100%;
height: 162px;
row-gap: 4px;
&-dsl {

View File

@@ -1,18 +1,20 @@
import { PREFIX_CLS } from '../../../common/constants';
import { formatMetric, formatNumberWithCN } from '../../../utils/utils';
import { AggregateInfoType } from '../../../common/type';
import { formatByDecimalPlaces, formatMetric, formatNumberWithCN } from '../../../utils/utils';
import { AggregateInfoType, ColumnType } from '../../../common/type';
import PeriodCompareItem from '../MetricCard/PeriodCompareItem';
import { SwapOutlined } from '@ant-design/icons';
import { useState } from 'react';
type Props = {
aggregateInfo: AggregateInfoType;
currentMetricField: ColumnType;
};
const MetricInfo: React.FC<Props> = ({ aggregateInfo }) => {
const MetricInfo: React.FC<Props> = ({ aggregateInfo, currentMetricField }) => {
const { metricInfos } = aggregateInfo || {};
const metricInfo = metricInfos?.[0] || {};
const { date, value, statistics } = metricInfo || {};
const { dataFormatType, dataFormat } = currentMetricField;
const prefixCls = `${PREFIX_CLS}-metric-info`;
@@ -26,11 +28,20 @@ const MetricInfo: React.FC<Props> = ({ aggregateInfo }) => {
<div className={`${prefixCls}-indicator`}>
<div style={{ display: 'flex', alignItems: 'flex-end' }}>
<div className={`${prefixCls}-indicator-value`}>
{isNumber ? formatMetric(value) : formatNumberWithCN(+value)}
</div>
<div className={`${prefixCls}-indicator-switch`}>
<SwapOutlined onClick={handleNumberClick} />
{dataFormatType === 'percent' || dataFormatType === 'decimal'
? `${formatByDecimalPlaces(
dataFormat?.needMultiply100 ? +value * 100 : value,
dataFormat?.decimalPlaces || 2
)}${dataFormatType === 'percent' ? '%' : ''}`
: isNumber
? formatMetric(value)
: formatNumberWithCN(+value)}
</div>
{!isNaN(+value) && +value >= 10000 && (
<div className={`${prefixCls}-indicator-switch`}>
<SwapOutlined onClick={handleNumberClick} />
</div>
)}
</div>
<div className={`${prefixCls}-bottom-section`}>
<div className={`${prefixCls}-date`}>

View File

@@ -147,11 +147,12 @@ const MetricTrendChart: React.FC<Props> = ({
}</span><span style="display: inline-block; width: 90px; text-align: right; font-weight: 500;">${
item.value === ''
? '-'
: metricField.dataFormatType === 'percent'
: metricField.dataFormatType === 'percent' ||
metricField.dataFormatType === 'decimal'
? `${formatByDecimalPlaces(
item.value,
metricField.dataFormat?.decimalPlaces || 2
)}%`
)}${metricField.dataFormatType === 'percent' ? '%' : ''}`
: getFormattedValue(item.value)
}</span></div>`
)
@@ -176,7 +177,8 @@ const MetricTrendChart: React.FC<Props> = ({
smooth: true,
data: data.map((item: any) => {
const value = item[valueColumnName];
return metricField.dataFormatType === 'percent' &&
return (metricField.dataFormatType === 'percent' ||
metricField.dataFormatType === 'decimal') &&
metricField.dataFormat?.needMultiply100
? value * 100
: value;

View File

@@ -177,7 +177,10 @@ const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApply
<Spin spinning={loading}>
<div className={`${prefixCls}-content`}>
{!isMobile && aggregateInfoValue?.metricInfos?.length > 0 && (
<MetricInfo aggregateInfo={aggregateInfoValue} />
<MetricInfo
aggregateInfo={aggregateInfoValue}
currentMetricField={currentMetricField}
/>
)}
<div className={`${prefixCls}-date-options`}>
{dateOptions.map((dateOption: { label: string; value: number }, index: number) => {

View File

@@ -92,7 +92,7 @@
display: flex;
flex-direction: column;
width: 100%;
row-gap: 4px;
row-gap: 8px;
}
&-metric-fields {
@@ -198,7 +198,7 @@
&-indicator-value {
color: var(--text-color);
font-weight: 500;
font-size: 36px;
font-size: 28px;
line-height: 40px;
margin-top: 2px;
color: var(--text-color-secondary);

View File

@@ -1,4 +1,3 @@
import { isMobile } from '../../utils/utils';
import Bar from './Bar';
import MetricCard from './MetricCard';
import MetricTrend from './MetricTrend';
@@ -10,14 +9,12 @@ import classNames from 'classnames';
import { PREFIX_CLS } from '../../common/constants';
type Props = {
question: string;
data: MsgDataType;
chartIndex: number;
isMobileMode?: boolean;
triggerResize?: boolean;
};
const ChatMsg: React.FC<Props> = ({ question, data, chartIndex, isMobileMode, triggerResize }) => {
const ChatMsg: React.FC<Props> = ({ data, chartIndex, triggerResize }) => {
const { queryColumns, queryResults, chatContext, queryMode } = data;
const [columns, setColumns] = useState<ColumnType[]>(queryColumns);
@@ -169,19 +166,6 @@ const ChatMsg: React.FC<Props> = ({ question, data, chartIndex, isMobileMode, tr
return <Table data={{ ...data, queryColumns: columns, queryResults: dataSource }} />;
};
// let width = '100%';
// if (isText) {
// width = 'fit-content';
// } else if (isMetricCard) {
// width = isDslMetricCard ? '290px' : '370px';
// } else if (categoryField.length > 1 && !isMobile && !isMobileMode) {
// if (columns.length === 1) {
// width = '600px';
// } else if (columns.length === 2) {
// width = '1000px';
// }
// }
const chartMsgClass = classNames({ [prefixCls]: !isTable });
return <div className={chartMsgClass}>{getMsgContent()}</div>;

View File

@@ -14,7 +14,6 @@
flex-wrap: wrap;
column-gap: 6px;
margin-top: 6px;
margin-bottom: 6px;
}
&-metric-card {

View File

@@ -1,7 +1,7 @@
import { createFromIconfontCN } from '@ant-design/icons';
const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_4120566_qiku6b2kol.js',
scriptUrl: '//at.alicdn.com/t/c/font_4120566_sz2crkuyuj.js',
});
export default IconFont;

View File

@@ -61,7 +61,6 @@ const Chat = () => {
agentId={6}
onMsgDataLoaded={onMsgDataLoaded}
isLastMessage
isMobileMode
triggerResize={triggerResize}
/>
</div>

View File

@@ -55,8 +55,8 @@ export function switchEntity(entityId: string, modelId?: number, chatId?: number
});
}
export function queryData(chatContext: ChatContextType) {
return axios.post<Result<QueryDataType>>(`${prefix}/chat/query/queryData`, chatContext);
export function queryData(chatContext: Partial<ChatContextType>) {
return axios.post<Result<MsgDataType>>(`${prefix}/chat/query/queryData`, chatContext);
}
export function queryContext(queryText: string, chatId?: number) {
@@ -95,3 +95,7 @@ export function updateQAFeedback(questionId: number, score: number) {
export function queryDrillDownDimensions(modelId: number) {
return axios.get<Result<{ dimensions: DrillDownDimensionType[] }>>(`${prefix}/chat/recommend/metric/${modelId}`);
}
export function queryDimensionValues(modelId: number, bizName: string, value: string) {
return axios.post<Result<any>>(`${prefix}/chat/query/queryDimensionValue`, { modelId, bizName, value});
}

View File

@@ -69,7 +69,7 @@ export const getFormattedValue = (value: number | string, remainZero?: boolean)
+value >= 100000000
? NumericUnit.OneHundredMillion
: +value >= 10000
? NumericUnit.EnTenThousand
? NumericUnit.TenThousand
: NumericUnit.None;
let formattedValue = formatByUnit(value, unit);
@@ -127,7 +127,7 @@ export const normalizeTrendData = (
) => {
const dateList = enumerateDaysBetweenDates(moment(startDate), moment(endDate), dateType);
const result = dateList.map((date) => {
const item = resultList.find((result) => result[dateColumnName] === date);
const item = resultList.find((result) => moment(result[dateColumnName]).format(dateType === 'months' ? 'YYYY-MM' : 'YYYY-MM-DD') === date);
return {
...(item || {}),
[dateColumnName]: date,
@@ -161,8 +161,9 @@ export function getLightenDarkenColor(col, amt) {
} else {
result = hexToRgbObj(col) || {};
}
return `rgba(${result.r + amt},${result.g + amt},${result.b + amt}${result.a ? `,${result.a}` : ''
})`;
return `rgba(${result.r + amt},${result.g + amt},${result.b + amt}${
result.a ? `,${result.a}` : ''
})`;
}
export function getChartLightenColor(col) {