(feature)(webapp) add filter modify and similar questions (#213)

This commit is contained in:
williamhliu
2023-10-13 18:31:00 +08:00
committed by GitHub
parent ab19b18169
commit 767abc2b90
27 changed files with 799 additions and 795 deletions

View File

@@ -9,7 +9,7 @@
"@ant-design/icons": "^4.7.0",
"@uiw/react-watermark": "^0.0.5",
"ahooks": "^3.7.8",
"antd": "^5.5.2",
"antd": "^4.23.5",
"axios": "^0.21.1",
"classnames": "^2.3.2",
"echarts": "^5.4.2",

View File

@@ -75,7 +75,6 @@ const MessageContainer: React.FC<Props> = ({
identityMsg,
msgData,
score,
parseOptions,
filters,
} = msgItem;
@@ -108,24 +107,6 @@ const MessageContainer: React.FC<Props> = ({
/>
</>
)}
{type === MessageTypeEnum.PARSE_OPTIONS && (
<ChatItem
msg={msgValue || msg || ''}
conversationId={chatId}
modelId={modelId}
agentId={agentId}
filter={filters}
isLastMessage={index === messageList.length - 1}
triggerResize={triggerResize}
parseOptions={parseOptions}
integrateSystem={integrateSystem}
onMsgDataLoaded={(data: MsgDataType, valid: boolean) => {
onMsgDataLoaded(data, msgId, msgValue || msg || '', valid);
}}
onUpdateMessageScroll={updateMessageContainerScroll}
onSendMsg={onSendMsg}
/>
)}
</div>
);
})}

View File

@@ -21,7 +21,7 @@ export enum SemanticTypeEnum {
}
export const SEMANTIC_TYPE_MAP = {
[SemanticTypeEnum.MODEL]: '数据来源',
[SemanticTypeEnum.MODEL]: '数据模型',
[SemanticTypeEnum.DIMENSION]: '维度',
[SemanticTypeEnum.METRIC]: '指标',
[SemanticTypeEnum.VALUE]: '维度值',

View File

@@ -309,21 +309,11 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
if (!data) {
return;
}
let parseOptionsItem: any;
if (data.parseOptions && data.parseOptions.length > 0) {
parseOptionsItem = {
id: uuid(),
msg: messageList[messageList.length - 1]?.msg,
type: MessageTypeEnum.PARSE_OPTIONS,
parseOptions: data.parseOptions,
};
}
const msgs = cloneDeep(messageList);
const msg = msgs.find(item => item.id === questionId);
if (msg) {
msg.msgData = data;
const msgList = [...msgs, ...(parseOptionsItem ? [parseOptionsItem] : [])];
setMessageList(msgList);
setMessageList(msgs);
}
updateMessageContainerScroll(`${questionId}`);
};

View File

@@ -1,4 +1,4 @@
import { ChatContextType, MsgDataType, SendMsgParamsType } from "../common/type";
import { MsgDataType, SendMsgParamsType } from "../common/type";
export enum MessageTypeEnum {
TEXT = 'text', // 指标文本
@@ -10,7 +10,6 @@ export enum MessageTypeEnum {
PLUGIN = 'PLUGIN', // 插件
WEB_PAGE = 'WEB_PAGE', // 插件
RECOMMEND_QUESTIONS = 'recommend_questions', // 推荐问题
PARSE_OPTIONS = 'parse_options', // 解析选项
AGENT_LIST = 'agent_list', // 专家列表
}
@@ -27,7 +26,6 @@ export type MessageItem = {
quote?: string;
score?: number;
feedback?: string;
parseOptions?: ChatContextType[];
filters?: any;
};

View File

@@ -9,6 +9,7 @@ export type SearchRecommendItem = {
export type FieldType = {
bizName: string;
itemId: number;
id: number;
name: string;
status: number;
@@ -86,7 +87,7 @@ export type ChatContextType = {
dimensions: FieldType[];
metrics: FieldType[];
entity: { alias: string[], id: number };
entityInfo: { dimensions: EntityDimensionType[] };
entityInfo: EntityInfoType;
elementMatches: any[];
nativeQuery: boolean;
queryMode: string;
@@ -133,6 +134,7 @@ export type MsgDataType = {
queryId: number;
queryMode: string;
queryState: string;
queryText: string;
response: PluginResonseType;
parseOptions?: ChatContextType[];
};

View File

@@ -1,10 +1,11 @@
import { Spin } from 'antd';
import { CheckCircleFilled } from '@ant-design/icons';
import { Button, Spin } from 'antd';
import { CheckCircleFilled, ReloadOutlined } from '@ant-design/icons';
import { PREFIX_CLS } from '../../common/constants';
import { MsgDataType } from '../../common/type';
import ChatMsg from '../ChatMsg';
import WebPage from '../ChatMsg/WebPage';
import Loading from './Loading';
import React, { ReactNode } from 'react';
type Props = {
queryId?: number;
@@ -12,8 +13,11 @@ type Props = {
entitySwitchLoading: boolean;
chartIndex: number;
executeTip?: string;
executeItemNode?: ReactNode;
renderCustomExecuteNode?: boolean;
data?: MsgDataType;
triggerResize?: boolean;
onRefresh: () => void;
};
const ExecuteItem: React.FC<Props> = ({
@@ -22,12 +26,15 @@ const ExecuteItem: React.FC<Props> = ({
entitySwitchLoading,
chartIndex,
executeTip,
executeItemNode,
renderCustomExecuteNode,
data,
triggerResize,
onRefresh,
}) => {
const prefixCls = `${PREFIX_CLS}-item`;
const getNodeTip = (title: string, tip?: string) => {
const getNodeTip = (title: ReactNode, tip?: string) => {
return (
<>
<div className={`${prefixCls}-title-bar`}>
@@ -42,12 +49,19 @@ const ExecuteItem: React.FC<Props> = ({
);
};
const reloadNode = (
<Button className={`${prefixCls}-reload`} size="small" onClick={onRefresh}>
<ReloadOutlined />
</Button>
);
if (executeLoading) {
return getNodeTip('数据查询中');
}
if (executeTip) {
return getNodeTip('数据查询失败', executeTip);
return getNodeTip(<>{reloadNode}</>, executeTip);
}
if (!data) {
@@ -58,14 +72,19 @@ const ExecuteItem: React.FC<Props> = ({
<>
<div className={`${prefixCls}-title-bar`}>
<CheckCircleFilled className={`${prefixCls}-step-icon`} />
<div className={`${prefixCls}-step-title`}></div>
<div className={`${prefixCls}-step-title`}>
{reloadNode}
</div>
</div>
<div className={`${prefixCls}-content-container ${prefixCls}-last-node`}>
<div className={`${prefixCls}-content-container`}>
<Spin spinning={entitySwitchLoading}>
{data.queryAuthorization?.message && (
<div className={`${prefixCls}-auth-tip`}>{data.queryAuthorization.message}</div>
)}
{data?.queryMode === 'WEB_PAGE' ? (
{renderCustomExecuteNode && executeItemNode ? (
executeItemNode
) : data?.queryMode === 'WEB_PAGE' ? (
<WebPage id={queryId!} data={data} />
) : (
<ChatMsg

View File

@@ -1,27 +1,46 @@
import { Select, Spin } from 'antd';
import { Select, Spin, InputNumber } from 'antd';
import { PREFIX_CLS } from '../../common/constants';
import { FilterItemType } from '../../common/type';
import { ChatContextType, FilterItemType } from '../../common/type';
import { useEffect, useMemo, useRef, useState } from 'react';
import { queryDimensionValues } from '../../service';
import debounce from 'lodash/debounce';
import isArray from 'lodash/isArray';
import { debounce, isArray } from 'lodash';
import SwicthEntity from './SwitchEntity';
type Props = {
modelId: number;
filters: FilterItemType[];
filter: FilterItemType;
chatContext: ChatContextType;
agentId?: number;
entityAlias?: string;
onFiltersChange: (filters: FilterItemType[]) => void;
onSwitchEntity: (entityId: string) => void;
};
const FilterItem: React.FC<Props> = ({ modelId, filters, filter, onFiltersChange }) => {
const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
const FilterItem: React.FC<Props> = ({
modelId,
filters,
filter,
chatContext,
agentId,
entityAlias,
onFiltersChange,
onSwitchEntity,
}) => {
const [options, setOptions] = useState<{ label: string; value: string | null }[]>([]);
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, '');
const { data } = await queryDimensionValues(
modelId,
filter.bizName,
agentId!,
filter.elementID,
''
);
setOptions(
data?.resultList.map((item: any) => ({
label: item[filter.bizName],
@@ -42,31 +61,56 @@ const FilterItem: React.FC<Props> = ({ modelId, filters, filter, onFiltersChange
const fetchId = fetchRef.current;
setOptions([]);
setLoading(true);
queryDimensionValues(modelId, filter.bizName, value).then(newOptions => {
if (fetchId !== fetchRef.current) {
return;
queryDimensionValues(modelId, filter.bizName, agentId!, filter.elementID, value).then(
newOptions => {
if (fetchId !== fetchRef.current) {
return;
}
setOptions(
newOptions.data?.resultList.map((item: any) => ({
label: item[filter.bizName],
value: item[filter.bizName],
})) || []
);
setLoading(false);
}
setOptions(
newOptions.data?.resultList.map((item: any) => ({
label: item[filter.bizName],
value: item[filter.bizName],
})) || []
);
setLoading(false);
});
);
};
return debounce(loadOptions, 800);
return debounce(loadOptions, 500);
}, [queryDimensionValues]);
const onChange = (value: string | string[]) => {
if (isArray(value) && value.length === 0) {
return;
}
const onOperatorChange = (value: string) => {
const newFilters = filters.map(item => {
if (item.bizName === filter.bizName) {
item.value = isArray(value) ? value : `${value}`;
item.operator = value;
}
return item;
});
onFiltersChange(newFilters);
};
const onChange = (value: string | string[] | number | null) => {
const newFilters = filters.map(item => {
if (item.bizName === filter.bizName) {
item.value =
typeof filter.value === 'number' || filter.value === null
? value
: isArray(value)
? value
: `${value}`;
if (isArray(value)) {
if (value.length === 1) {
item.operator = '=';
item.value = value[0];
} else {
item.operator = 'IN';
item.value = value;
}
} else {
item.value =
typeof filter.value === 'number' || filter.value === null ? value : `${value}`;
}
}
return item;
});
@@ -75,18 +119,46 @@ const FilterItem: React.FC<Props> = ({ modelId, filters, filter, onFiltersChange
return (
<span className={prefixCls}>
{(typeof filter.value === 'string' || isArray(filter.value)) &&
(filter.operator === '=' || filter.operator === 'IN') ? (
<span className={`${prefixCls}-filter-name`}>{filter.name}</span>
{(typeof filter.value === 'number' || filter.value === null) &&
!filter.bizName?.includes('_id') ? (
<>
<Select
options={[
{ label: '大于', value: '>' },
{ label: '等于', value: '=' },
{ label: '小于', value: '<' },
]}
className={`${prefixCls}-operator-control`}
value={filter.operator}
onChange={onOperatorChange}
/>
<InputNumber
className={`${prefixCls}-input-number-control`}
value={filter.value}
onChange={onChange}
/>
</>
) : (typeof filter.value === 'string' || isArray(filter.value)) &&
!filter.bizName?.includes('_id') ? (
<Select
bordered={false}
value={filter.value}
options={options}
options={options.filter(option => option.value !== '' && option.value !== null)}
className={`${prefixCls}-select-control`}
onSearch={debounceFetcher}
notFoundContent={loading ? <Spin size="small" /> : null}
onChange={onChange}
mode={isArray(filter.value) ? 'multiple' : undefined}
showSearch
// allowClear
/>
) : entityAlias &&
['歌曲', '艺人'].includes(entityAlias) &&
filter.bizName?.includes('_id') ? (
<SwicthEntity
entityName={filter.value}
chatContext={chatContext}
onSwitchEntity={onSwitchEntity}
/>
) : (
<span className={`${prefixCls}-filter-value`}>{filter.value}</span>

View File

@@ -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>,

View File

@@ -1,24 +1,44 @@
import { CheckCircleFilled, DownOutlined, UpOutlined } from '@ant-design/icons';
import { CheckCircleFilled, DownOutlined, LoadingOutlined, UpOutlined } from '@ant-design/icons';
import { PREFIX_CLS } from '../../common/constants';
import { SimilarQuestionType } from '../../common/type';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { querySimilarQuestions } from '../../service';
type Props = {
similarQuestions: SimilarQuestionType[];
// similarQuestions: SimilarQuestionType[];
queryText: string;
agentId?: number;
defaultExpanded?: boolean;
onSelectQuestion: (question: SimilarQuestionType) => void;
};
const SimilarQuestions: React.FC<Props> = ({
similarQuestions,
// similarQuestions,
queryText,
agentId,
defaultExpanded,
onSelectQuestion,
}) => {
const [similarQuestions, setSimilarQuestions] = useState<SimilarQuestionType[]>([]);
const [expanded, setExpanded] = useState(defaultExpanded || false);
const [loading, setLoading] = useState(false);
const tipPrefixCls = `${PREFIX_CLS}-item`;
const prefixCls = `${PREFIX_CLS}-similar-questions`;
const initData = async () => {
setLoading(true);
const res = await querySimilarQuestions(queryText, agentId);
setLoading(false);
setSimilarQuestions(res.data || []);
};
useEffect(() => {
if (expanded && similarQuestions?.length === 0) {
initData();
}
}, [expanded]);
const onToggleExpanded = () => {
setExpanded(!expanded);
};
@@ -30,14 +50,14 @@ const SimilarQuestions: React.FC<Props> = ({
<div className={`${tipPrefixCls}-step-title`}>
<span className={`${prefixCls}-toggle-expand-btn`} onClick={onToggleExpanded}>
{expanded ? <UpOutlined /> : <DownOutlined />}
{loading ? <LoadingOutlined /> : expanded ? <UpOutlined /> : <DownOutlined />}
</span>
</div>
</div>
<div className={prefixCls}>
{expanded && (
<div className={`${prefixCls}-content`}>
{similarQuestions.slice(0, 5).map((question, index) => {
{similarQuestions?.slice(0, 5).map((question, index) => {
return (
<div
className={`${prefixCls}-question`}

View File

@@ -36,18 +36,18 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo }) => {
<div className={`${tipPrefixCls}-title-bar`}>
<CheckCircleFilled className={`${tipPrefixCls}-step-icon`} />
<div className={`${tipPrefixCls}-step-title`}>
SQL生成
SQL生成
{sqlType && (
<span className={`${prefixCls}-toggle-expand-btn`} onClick={onCollapse}>
<UpOutlined />
</span>
)}
</div>
<div className={`${prefixCls}-sql-options`}>
<div className={`${tipPrefixCls}-content-options`}>
{sqlInfo.llmParseSql && (
<div
className={`${prefixCls}-sql-option ${
sqlType === 'llmParseSql' ? `${prefixCls}-sql-option-active` : ''
className={`${tipPrefixCls}-content-option ${
sqlType === 'llmParseSql' ? `${tipPrefixCls}-content-option-active` : ''
}`}
onClick={() => {
setSqlType(sqlType === 'llmParseSql' ? '' : 'llmParseSql');
@@ -58,8 +58,8 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo }) => {
)}
{sqlInfo.logicSql && (
<div
className={`${prefixCls}-sql-option ${
sqlType === 'logicSql' ? `${prefixCls}-sql-option-active` : ''
className={`${tipPrefixCls}-content-option ${
sqlType === 'logicSql' ? `${tipPrefixCls}-content-option-active` : ''
}`}
onClick={() => {
setSqlType(sqlType === 'logicSql' ? '' : 'logicSql');
@@ -70,8 +70,8 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo }) => {
)}
{sqlInfo.querySql && (
<div
className={`${prefixCls}-sql-option ${
sqlType === 'querySql' ? `${prefixCls}-sql-option-active` : ''
className={`${tipPrefixCls}-content-option ${
sqlType === 'querySql' ? `${tipPrefixCls}-content-option-active` : ''
}`}
onClick={() => {
setSqlType(sqlType === 'querySql' ? '' : 'querySql');

View File

@@ -1,12 +1,14 @@
import {
ChatContextType,
DateInfoType,
EntityInfoType,
FilterItemType,
MsgDataType,
ParseStateEnum,
SimilarQuestionType,
} from '../../common/type';
import { useEffect, useState } from 'react';
import { chatExecute, chatParse, queryData, switchEntity } from '../../service';
import { chatExecute, chatParse, queryData, queryEntityInfo, switchEntity } from '../../service';
import { PARSE_ERROR_TIP, PREFIX_CLS, SEARCH_EXCEPTION_TIP } from '../../common/constants';
import IconFont from '../IconFont';
import ParseTip from './ParseTip';
@@ -16,6 +18,7 @@ import classNames from 'classnames';
import Tools from '../Tools';
import SqlItem from './SqlItem';
import SimilarQuestionItem from './SimilarQuestionItem';
import moment from 'moment';
type Props = {
msg: string;
@@ -26,9 +29,10 @@ type Props = {
isLastMessage?: boolean;
msgData?: MsgDataType;
triggerResize?: boolean;
parseOptions?: ChatContextType[];
isDeveloper?: boolean;
integrateSystem?: string;
executeItemNode?: React.ReactNode;
renderCustomExecuteNode?: boolean;
onMsgDataLoaded?: (data: MsgDataType, valid: boolean) => void;
onUpdateMessageScroll?: () => void;
onSendMsg?: (msg: string) => void;
@@ -43,9 +47,10 @@ const ChatItem: React.FC<Props> = ({
isLastMessage,
triggerResize,
msgData,
parseOptions,
isDeveloper,
integrateSystem,
executeItemNode,
renderCustomExecuteNode,
onMsgDataLoaded,
onUpdateMessageScroll,
onSendMsg,
@@ -53,13 +58,15 @@ const ChatItem: React.FC<Props> = ({
const [data, setData] = useState<MsgDataType>();
const [parseLoading, setParseLoading] = useState(false);
const [parseInfo, setParseInfo] = useState<ChatContextType>();
const [parseInfoOptions, setParseInfoOptions] = useState<ChatContextType[]>(parseOptions || []);
const [parseInfoOptions, setParseInfoOptions] = useState<ChatContextType[]>([]);
const [parseTip, setParseTip] = useState('');
const [executeLoading, setExecuteLoading] = useState(false);
const [executeTip, setExecuteTip] = useState('');
const [executeMode, setExecuteMode] = useState(false);
const [entitySwitchLoading, setEntitySwitchLoading] = useState(false);
const [similarQuestions, setSimilarQuestions] = useState<SimilarQuestionType[]>([]);
const [dimensionFilters, setDimensionFilters] = useState<FilterItemType[]>([]);
const [dateInfo, setDateInfo] = useState<DateInfoType>({} as DateInfoType);
const [entityInfo, setEntityInfo] = useState<EntityInfoType>({} as EntityInfoType);
const [chartIndex, setChartIndex] = useState(0);
@@ -95,15 +102,13 @@ const ChatItem: React.FC<Props> = ({
const res: any = await chatExecute(msg, conversationId!, parseInfoValue);
setExecuteLoading(false);
const valid = updateData(res);
if (onMsgDataLoaded) {
onMsgDataLoaded(
{
...res.data,
chatContext: parseInfoValue,
},
valid
);
}
onMsgDataLoaded?.(
{
...res.data,
chatContext: parseInfoValue,
},
valid
);
} catch (e) {
setExecuteLoading(false);
setExecuteTip(SEARCH_EXCEPTION_TIP);
@@ -115,8 +120,7 @@ const ChatItem: React.FC<Props> = ({
const parseData: any = await chatParse(msg, conversationId, modelId, agentId, filter);
setParseLoading(false);
const { code, data } = parseData || {};
const { state, selectedParses, queryId, similarSolvedQuery } = data || {};
setSimilarQuestions(similarSolvedQuery || []);
const { state, selectedParses, candidateParses, queryId } = data || {};
if (
code !== 200 ||
state === ParseStateEnum.FAILED ||
@@ -126,27 +130,33 @@ const ChatItem: React.FC<Props> = ({
setParseTip(PARSE_ERROR_TIP);
return;
}
if (onUpdateMessageScroll) {
onUpdateMessageScroll();
}
const parseInfos = selectedParses.map((item: any) => ({
...item,
queryId,
}));
onUpdateMessageScroll?.();
const parseInfos = selectedParses
.concat(candidateParses || [])
.slice(0, 5)
.map((item: any) => ({
...item,
queryId,
}));
setParseInfoOptions(parseInfos || []);
const parseInfoValue = parseInfos[0];
setParseInfo(parseInfoValue);
setEntityInfo(parseInfoValue.entityInfo || {});
setDimensionFilters(parseInfoValue?.dimensionFilters || []);
setDateInfo(parseInfoValue?.dateInfo);
onExecute(parseInfoValue);
};
useEffect(() => {
if (data !== undefined || parseOptions !== undefined || executeTip !== '' || parseLoading) {
if (data !== undefined || executeTip !== '' || parseLoading) {
return;
}
if (msgData) {
const parseInfoValue = { ...msgData.chatContext, queryId: msgData.queryId };
setParseInfoOptions([parseInfoValue]);
setParseInfo(parseInfoValue);
setDimensionFilters(msgData.chatContext?.dimensionFilters || []);
setDateInfo(msgData.chatContext?.dateInfo);
setExecuteMode(true);
updateData({ code: 200, data: msgData, msg: 'success' });
} else if (msg) {
@@ -159,14 +169,31 @@ const ChatItem: React.FC<Props> = ({
const res = await switchEntity(entityId, data?.chatContext?.modelId, conversationId || 0);
setEntitySwitchLoading(false);
setData(res.data);
const { chatContext } = res.data;
setParseInfo(chatContext);
setParseInfoOptions([chatContext]);
const { chatContext, entityInfo } = res.data;
const chatContextValue = { ...(chatContext || {}), queryId: parseInfo?.queryId };
setParseInfo(chatContextValue);
setEntityInfo(entityInfo);
setDimensionFilters(chatContextValue?.dimensionFilters || []);
setDateInfo(chatContextValue?.dateInfo);
};
const onFiltersChange = async (dimensionFilters: FilterItemType[]) => {
const onFiltersChange = (dimensionFilters: FilterItemType[]) => {
setDimensionFilters(dimensionFilters);
};
const onDateInfoChange = (dateRange: any) => {
setDateInfo({
...(dateInfo || {}),
startDate: moment(dateRange[0]).format('YYYY-MM-DD'),
endDate: moment(dateRange[1]).format('YYYY-MM-DD'),
dateMode: 'BETWEEN',
unit: 0,
});
};
const onRefresh = async () => {
setEntitySwitchLoading(true);
const { dimensions, metrics, dateInfo, id, queryId } = parseInfoOptions[0] || {};
const { dimensions, metrics, id, queryId } = parseInfo || {};
const chatContextValue = {
dimensions,
metrics,
@@ -177,18 +204,33 @@ const ChatItem: React.FC<Props> = ({
};
const res: any = await queryData(chatContextValue);
setEntitySwitchLoading(false);
const resChatContext = res.data?.chatContext;
setData({ ...(res.data || {}), chatContext: resChatContext || chatContextValue });
setParseInfo(resChatContext || chatContextValue);
setParseInfoOptions([resChatContext || chatContextValue]);
if (res.code === 200) {
const resChatContext = res.data?.chatContext;
const contextValue = { ...(resChatContext || chatContextValue), queryId };
const dataValue = { ...res.data, chatContext: contextValue };
if (onMsgDataLoaded) {
onMsgDataLoaded(dataValue, true);
}
setData(dataValue);
setParseInfo(contextValue);
}
};
const getEntityInfo = async (parseInfoValue: ChatContextType) => {
const res = await queryEntityInfo(parseInfoValue.queryId, parseInfoValue.id);
setEntityInfo(res.data);
};
const onSelectParseInfo = async (parseInfoValue: ChatContextType) => {
setParseInfo(parseInfoValue);
onExecute(parseInfoValue);
if (onUpdateMessageScroll) {
onUpdateMessageScroll();
setDimensionFilters(parseInfoValue.dimensionFilters || []);
setDateInfo(parseInfoValue.dateInfo);
if (parseInfoValue.entityInfo) {
setEntityInfo(parseInfoValue.entityInfo);
} else {
getEntityInfo(parseInfoValue);
}
onUpdateMessageScroll?.();
};
const onSelectQuestion = (question: SimilarQuestionType) => {
@@ -205,36 +247,29 @@ const ChatItem: React.FC<Props> = ({
return (
<div className={prefixCls}>
{!isMobile && <IconFont type="icon-zhinengsuanfa" className={`${prefixCls}-avatar`} />}
{!isMobile && integrateSystem !== 'wiki' && (
<IconFont type="icon-zhinengsuanfa" className={`${prefixCls}-avatar`} />
)}
<div className={isMobile ? `${prefixCls}-mobile-msg-card` : `${prefixCls}-msg-card`}>
<div className={contentClass}>
<ParseTip
parseLoading={parseLoading}
parseInfoOptions={parseOptions || parseInfoOptions.slice(0, 1)}
parseInfoOptions={parseInfoOptions}
parseTip={parseTip}
currentParseInfo={parseInfo}
agentId={agentId}
dimensionFilters={dimensionFilters}
dateInfo={dateInfo}
entityInfo={entityInfo}
onSelectParseInfo={onSelectParseInfo}
onSwitchEntity={onSwitchEntity}
onFiltersChange={onFiltersChange}
onDateInfoChange={onDateInfoChange}
/>
{parseTip && similarQuestions.length > 0 && (
<SimilarQuestionItem
similarQuestions={similarQuestions}
defaultExpanded
onSelectQuestion={onSelectQuestion}
/>
)}
{executeMode && (
<>
{parseInfoOptions?.[0]?.sqlInfo && isDeveloper && integrateSystem !== 'c2' && (
<SqlItem integrateSystem={integrateSystem} sqlInfo={parseInfoOptions[0].sqlInfo} />
)}
{similarQuestions.length > 0 && (
<SimilarQuestionItem
similarQuestions={similarQuestions}
defaultExpanded={executeTip !== ''}
onSelectQuestion={onSelectQuestion}
/>
{parseInfo?.sqlInfo && isDeveloper && integrateSystem !== 'c2' && (
<SqlItem integrateSystem={integrateSystem} sqlInfo={parseInfo.sqlInfo} />
)}
<ExecuteItem
queryId={parseInfo?.queryId}
@@ -244,12 +279,27 @@ const ChatItem: React.FC<Props> = ({
chartIndex={chartIndex}
data={data}
triggerResize={triggerResize}
executeItemNode={executeItemNode}
renderCustomExecuteNode={renderCustomExecuteNode}
onRefresh={onRefresh}
/>
</>
)}
{(parseTip !== '' || (executeMode && !executeLoading)) && integrateSystem !== 'c2' && (
<SimilarQuestionItem
queryText={msg || msgData?.queryText || ''}
agentId={agentId}
defaultExpanded={parseTip !== '' || executeTip !== '' || integrateSystem === 'wiki'}
onSelectQuestion={onSelectQuestion}
/>
)}
</div>
{!isMetricCard && data && (
<Tools data={data} scoreValue={undefined} isLastMessage={isLastMessage} />
<Tools
queryId={parseInfo?.queryId || 0}
scoreValue={undefined}
isLastMessage={isLastMessage}
/>
)}
</div>
</div>

View File

@@ -52,6 +52,30 @@
}
}
&-content-options {
display: flex;
align-items: center;
column-gap: 13px;
margin-left: -10px;
}
&-content-option {
border-radius: 4px;
padding: 0 4px;
font-weight: normal;
color: var(--text-color-third);
cursor: pointer;
&:hover {
color: var(--chat-blue);
}
}
&-content-option-active {
color: #fff !important;
background-color: var(--chat-blue);
}
&-avatar {
display: flex;
align-items: center;
@@ -93,12 +117,34 @@
margin-top: 12px;
}
.ant-picker {
background-color: #f5f8fb !important;
border-color: #ececec !important;
}
.ant-picker-input > input {
color: var(--chat-blue);
font-weight: 500;
}
&-title-bar {
display: flex;
align-items: center;
column-gap: 10px;
}
&-step-title {
font-weight: 500;
color: var(--text-color);
}
&-reload {
margin-left: 2px;
font-weight: normal;
color: var(--text-color-secondary);
font-size: 13px !important;
}
&-step-icon {
color: var(--green);
font-size: 16px;
@@ -106,10 +152,7 @@
&-content-container {
margin: 2px 0 2px 7px;
padding: 6px 0 4px 18px;
}
&-content-container-succeed {
padding: 10px 0 4px 18px;
border-left: 1px solid var(--green);
padding-bottom: 10px;
}
@@ -127,6 +170,7 @@
column-gap: 6px;
color: var(--text-color-fourth);
font-size: 13px;
font-weight: normal;
}
&-switch-entity {
@@ -213,6 +257,8 @@
&-tip-item {
display: flex;
align-items: center;
flex-wrap: wrap;
row-gap: 4px;
}
&-tip-item-content {
@@ -225,10 +271,12 @@
align-items: center;
flex-wrap: wrap;
column-gap: 12px;
row-gap: 6px;
}
&-tip-item-filter-name {
color: var(--text-color-secondary);
font-weight: 500;
}
&-mode-name {
@@ -241,10 +289,6 @@
font-weight: 500;
}
&-tip-item-option {
font-weight: 500;
}
&-entity-info {
display: flex;
align-items: center;
@@ -305,13 +349,35 @@
}
.@{filter-item-prefix-cls} {
display: flex;
align-items: center;
font-weight: 500;
&-filter-name {
color: var(--text-color-secondary);
}
&-select-control {
min-width: 120px;
background-color: #f5f8fb;
border-radius: 6px;
}
.ant-select-selection-item {
&-operator-control {
min-width: 80px;
border-radius: 6px;
margin-right: 8px;
}
&-input-number-control {
min-width: 100px;
}
.ant-select-selector, .ant-input-number-input {
background-color: #f5f8fb !important;
border-color: #ececec !important;
}
.ant-select-selection-item, .ant-input-number-input {
color: var(--chat-blue);
font-weight: 500;
}
@@ -334,34 +400,11 @@
overflow: auto;
&-toggle-expand-btn {
margin-left: 4px;
color: var(--text-color-fourth);
font-size: 12px;
margin-right: 10px;
cursor: pointer;
}
&-sql-options {
margin-left: 4px;
display: flex;
align-items: center;
column-gap: 13px;
color: var(--text-color-third);
}
&-sql-option {
border-radius: 4px;
padding: 1px 4px;
cursor: pointer;
&:hover {
color: var(--chat-blue);
}
}
&-sql-option-active {
color: #fff !important;
background-color: var(--chat-blue);
}
&-code {
margin-top: 10px !important;
@@ -373,7 +416,7 @@
&-copy-btn {
position: absolute;
top: 30px;
top: 24px;
right: 20px;
background: transparent !important;
border: 0 !important;
@@ -390,7 +433,6 @@
position: relative;
margin: 2px 0 2px 7px;
padding: 2px 0 8px 18px;
border-left: 1px solid var(--green);
overflow: auto;
&-toggle-expand-btn {
@@ -403,7 +445,7 @@
&-content {
display: flex;
flex-direction: column;
row-gap: 12px;
row-gap: 8px;
margin-top: 6px;
margin-bottom: 2px;
}

View File

@@ -68,7 +68,7 @@ const Message: React.FC<Props> = ({
<div className={`${prefixCls}-info-bar`}>
<div className={`${prefixCls}-main-entity-info`}>
<div className={`${prefixCls}-info-item`}>
<div className={`${prefixCls}-info-name`}></div>
<div className={`${prefixCls}-info-name`}></div>
<div className={`${prefixCls}-info-value`}>{modelName}</div>
</div>
<div className={`${prefixCls}-info-item`}>

View File

@@ -4,6 +4,7 @@ import { MsgDataType } from '../../../common/type';
import { CLS_PREFIX } from '../../../common/constants';
import ApplyAuth from '../ApplyAuth';
import { SizeType } from 'antd/es/config-provider/SizeContext';
import moment from 'moment';
type Props = {
data: MsgDataType;

View File

@@ -86,7 +86,6 @@
.@{table-prefix-cls}-formatted-value {
font-weight: 500;
font-size: 16px;
}
.ant-table-thead .ant-table-cell {

View File

@@ -1,5 +1,4 @@
import { useCallback, useEffect, useState } from 'react';
import { CLS_PREFIX } from '../../../common/constants';
import { MsgDataType } from '../../../common/type';
import { getToken, isProd } from '../../../utils/utils';
@@ -14,8 +13,6 @@ const WebPage: React.FC<Props> = ({ id, data }) => {
const [pluginUrl, setPluginUrl] = useState('');
const [height, setHeight] = useState(DEFAULT_HEIGHT);
const prefixCls = `${CLS_PREFIX}-web-page`;
const {
name,
webPage: { url, params },

View File

@@ -129,7 +129,6 @@ const ChatMsg: React.FC<Props> = ({ queryId, data, chartIndex, triggerResize })
const onLoadData = async (value: any) => {
setLoading(true);
const res: any = await queryData({
// ...chatContext,
queryId,
parseId: chatContext.id,
...value,
@@ -200,7 +199,7 @@ const ChatMsg: React.FC<Props> = ({ queryId, data, chartIndex, triggerResize })
const existDrillDownDimension = queryMode.includes('METRIC') && !isText && !isEntityMode;
const isMultipleMetric = existDrillDownDimension && chatContext?.metrics?.length > 1;
const isMultipleMetric = queryMode.includes('METRIC') && chatContext?.metrics?.length > 1;
return (
<div className={chartMsgClass}>

View File

@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import { CLS_PREFIX } from '../../common/constants';
import { DrillDownDimensionType, FilterItemType } from '../../common/type';
import { queryDrillDownDimensions } from '../../service';
import { Dropdown } from 'antd';
import { Dropdown, Menu } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import classNames from 'classnames';
@@ -87,26 +87,27 @@ const DrillDownDimensions: React.FC<Props> = ({
<div>
<span></span>
<Dropdown
menu={{
items: dimensions.slice(DEFAULT_DIMENSION_COUNT).map(dimension => {
const itemNameClass = classNames({
[`${prefixCls}-menu-item-active`]: drillDownDimension?.id === dimension.id,
});
return {
label: (
<span
className={itemNameClass}
onClick={() => {
onSelectDimension(dimension);
}}
>
{dimension.name}
</span>
),
key: dimension.id,
};
}),
}}
overlay={
<Menu>
{dimensions.slice(DEFAULT_DIMENSION_COUNT).map(dimension => {
const itemNameClass = classNames({
[`${prefixCls}-menu-item-active`]: drillDownDimension?.id === dimension.id,
});
return (
<Menu.Item key={dimension.id}>
<span
className={itemNameClass}
onClick={() => {
onSelectDimension(dimension);
}}
>
{dimension.name}
</span>
</Menu.Item>
);
})}
</Menu>
}
>
<span>
<span className={`${prefixCls}-content-item-name`}></span>

View File

@@ -1,28 +1,21 @@
import { isMobile } from '../../utils/utils';
import { DislikeOutlined, LikeOutlined } from '@ant-design/icons';
import { CLS_PREFIX } from '../../common/constants';
import { MsgDataType } from '../../common/type';
import { useState } from 'react';
import classNames from 'classnames';
import { updateQAFeedback } from '../../service';
type Props = {
data: MsgDataType;
queryId: number;
scoreValue?: number;
isLastMessage?: boolean;
};
const Tools: React.FC<Props> = ({ data, scoreValue, isLastMessage }) => {
const { queryResults, queryId, chatContext, queryMode } = data || {};
const Tools: React.FC<Props> = ({ queryId, scoreValue, isLastMessage }) => {
const [score, setScore] = useState(scoreValue || 0);
const prefixCls = `${CLS_PREFIX}-tools`;
const singleData = queryResults?.length === 1;
const isMetricCard =
queryMode.includes('METRIC') &&
(singleData || chatContext?.dateInfo?.startDate === chatContext?.dateInfo?.endDate);
const like = () => {
setScore(5);
updateQAFeedback(queryId, 5);

View File

@@ -2,7 +2,7 @@ import { Input } from 'antd';
import styles from './style.module.less';
import { useEffect, useState } from 'react';
import ChatItem from '../components/ChatItem';
import { queryContext, searchRecommend } from '../service';
import { searchRecommend } from '../service';
const { Search } = Input;

View File

@@ -10,6 +10,10 @@
box-sizing: border-box;
}
.chatDemo {
height: 100%;
.copilotDemo {
padding: 20px;
}
.chatDemo {
height: 100vh;
}

View File

@@ -35,6 +35,8 @@ export type {
SendMsgParamsType,
} from './common/type';
export { getHistoryMsg, searchRecommend, queryContext } from './service';
export { searchRecommend } from './service';
export { saveConversation, getAllConversations } from './Chat/service';
export { setToken } from './utils/utils';

View File

@@ -1,5 +1,5 @@
import axios from './axiosInstance';
import { ChatContextType, DrillDownDimensionType, HistoryType, MsgDataType, ParseDataType, SearchRecommendItem } from '../common/type';
import { ChatContextType, DrillDownDimensionType, EntityInfoType, HistoryType, MsgDataType, ParseDataType, SearchRecommendItem } from '../common/type';
const DEFAULT_CHAT_ID = 0;
@@ -58,13 +58,6 @@ export function queryData(chatContext: Partial<ChatContextType>) {
return axios.post<MsgDataType>(`${prefix}/chat/query/queryData`, chatContext);
}
export function queryContext(queryText: string, chatId?: number) {
return axios.post<ChatContextType>(`${prefix}/chat/query/queryContext`, {
queryText,
chatId: chatId || DEFAULT_CHAT_ID,
});
}
export function getHistoryMsg(current: number, chatId: number = DEFAULT_CHAT_ID, pageSize: number = 10) {
return axios.post<HistoryType>(`${prefix}/chat/manage/pageQueryInfo?chatId=${chatId}`, {
current,
@@ -72,14 +65,6 @@ export function getHistoryMsg(current: number, chatId: number = DEFAULT_CHAT_ID,
});
}
export function saveConversation(chatName: string) {
return axios.post<any>(`${prefix}/chat/manage/save?chatName=${chatName}`);
}
export function getAllConversations() {
return axios.get<any>(`${prefix}/chat/manage/getAll`);
}
export function queryEntities(entityId: string | number, modelId: number) {
return axios.post<any>(`${prefix}/chat/query/choice`, {
entityId,
@@ -95,6 +80,14 @@ export function queryDrillDownDimensions(modelId: number) {
return axios.get<{ dimensions: DrillDownDimensionType[] }>(`${prefix}/chat/recommend/metric/${modelId}`);
}
export function queryDimensionValues(modelId: number, bizName: string, value: string) {
return axios.post<any>(`${prefix}/chat/query/queryDimensionValue`, { modelId, bizName, value});
export function queryDimensionValues(modelId: number, bizName: string, agentId: number, elementID: number, value: string) {
return axios.post<any>(`${prefix}/chat/query/queryDimensionValue`, { modelId, bizName, agentId, elementID, value});
}
export function querySimilarQuestions(queryText: string, agentId?: number) {
return axios.get<any>(`${prefix}/chat/manage/getSolvedQuery?queryText=${queryText}&agentId=${agentId}`);
}
export function queryEntityInfo(queryId: number, parseId: number) {
return axios.get<EntityInfoType>(`${prefix}/chat/query/getEntityInfo?queryId=${queryId}&parseId=${parseId}`)
}

View File

@@ -1,3 +1,5 @@
@import '~antd/dist/antd.css';
@import './index.less';
@prefix-cls: ~'@{supersonic-chat-prefix}';

View File

@@ -7,7 +7,7 @@
"jsx": "react-jsx",
"moduleResolution":"Node",
"allowSyntheticDefaultImports": true,
"importHelpers": true
"importHelpers": true,
},
"include": [
"src"

733
webapp/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff