mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-11 12:07:42 +00:00
Merge pull request #31 from williamhliu/master
[feature](webapp) add copilot and modify domain to model
This commit is contained in:
@@ -22,7 +22,7 @@ module.exports = function (proxy, allowedHost) {
|
|||||||
// https://github.com/webpack/webpack-dev-server/issues/887
|
// https://github.com/webpack/webpack-dev-server/issues/887
|
||||||
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
|
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
|
||||||
// However, it made several existing use cases such as development in cloud
|
// However, it made several existing use cases such as development in cloud
|
||||||
// environment or subdomains in development significantly more complicated:
|
// environment or submodels in development significantly more complicated:
|
||||||
// https://github.com/facebook/create-react-app/issues/2271
|
// https://github.com/facebook/create-react-app/issues/2271
|
||||||
// https://github.com/facebook/create-react-app/issues/2233
|
// https://github.com/facebook/create-react-app/issues/2233
|
||||||
// While we're investigating better solutions, for now we will take a
|
// While we're investigating better solutions, for now we will take a
|
||||||
@@ -33,7 +33,7 @@ module.exports = function (proxy, allowedHost) {
|
|||||||
// So we will disable the host check normally, but enable it if you have
|
// So we will disable the host check normally, but enable it if you have
|
||||||
// specified the `proxy` setting. Finally, we let you override it if you
|
// specified the `proxy` setting. Finally, we let you override it if you
|
||||||
// really know what you're doing with a special environment variable.
|
// really know what you're doing with a special environment variable.
|
||||||
// Note: ["localhost", ".localhost"] will support subdomains - but we might
|
// Note: ["localhost", ".localhost"] will support submodels - but we might
|
||||||
// want to allow setting the allowedHosts manually for more complex setups
|
// want to allow setting the allowedHosts manually for more complex setups
|
||||||
allowedHosts: disableFirewall ? 'all' : [allowedHost],
|
allowedHosts: disableFirewall ? 'all' : [allowedHost],
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export type SearchRecommendItem = {
|
export type SearchRecommendItem = {
|
||||||
complete: boolean;
|
complete: boolean;
|
||||||
domainId: number;
|
modelId: number;
|
||||||
domainName: string;
|
modelName: string;
|
||||||
recommend: string;
|
recommend: string;
|
||||||
subRecommend: string;
|
subRecommend: string;
|
||||||
schemaElementType: string;
|
schemaElementType: string;
|
||||||
@@ -12,12 +12,12 @@ export type FieldType = {
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
status: number;
|
status: number;
|
||||||
domain: number;
|
model: number;
|
||||||
type: string;
|
type: string;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DomainInfoType = {
|
export type ModelInfoType = {
|
||||||
bizName: string;
|
bizName: string;
|
||||||
itemId: number;
|
itemId: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -27,7 +27,7 @@ export type DomainInfoType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type EntityInfoType = {
|
export type EntityInfoType = {
|
||||||
domainInfo: DomainInfoType;
|
modelInfo: ModelInfoType;
|
||||||
dimensions: FieldType[];
|
dimensions: FieldType[];
|
||||||
metrics: FieldType[];
|
metrics: FieldType[];
|
||||||
entityId: number;
|
entityId: number;
|
||||||
@@ -53,8 +53,8 @@ export type FilterItemType = {
|
|||||||
|
|
||||||
export type ChatContextType = {
|
export type ChatContextType = {
|
||||||
aggType: string;
|
aggType: string;
|
||||||
domainId: number;
|
modelId: number;
|
||||||
domainName: string;
|
modelName: string;
|
||||||
dateInfo: DateInfoType;
|
dateInfo: DateInfoType;
|
||||||
dimensions: FieldType[];
|
dimensions: FieldType[];
|
||||||
metrics: FieldType[];
|
metrics: FieldType[];
|
||||||
@@ -104,6 +104,7 @@ export type MsgDataType = {
|
|||||||
queryMode: string;
|
queryMode: string;
|
||||||
queryState: string;
|
queryState: string;
|
||||||
response: PluginResonseType;
|
response: PluginResonseType;
|
||||||
|
parseOptions?: ChatContextType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum ParseStateEnum {
|
export enum ParseStateEnum {
|
||||||
@@ -121,6 +122,7 @@ export type ParseDataType = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type QueryDataType = {
|
export type QueryDataType = {
|
||||||
|
aggregateInfo: AggregateInfoType;
|
||||||
queryColumns: ColumnType[];
|
queryColumns: ColumnType[];
|
||||||
queryResults: any[];
|
queryResults: any[];
|
||||||
};
|
};
|
||||||
@@ -153,7 +155,7 @@ export const SEMANTIC_TYPE_MAP = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type SuggestionItemType = {
|
export type SuggestionItemType = {
|
||||||
domain: number;
|
model: number;
|
||||||
name: string;
|
name: string;
|
||||||
bizName: string
|
bizName: string
|
||||||
};
|
};
|
||||||
@@ -187,7 +189,7 @@ export type HistoryType = {
|
|||||||
|
|
||||||
export type DrillDownDimensionType = {
|
export type DrillDownDimensionType = {
|
||||||
id: number;
|
id: number;
|
||||||
domain: number;
|
model: number;
|
||||||
name: string;
|
name: string;
|
||||||
bizName: string;
|
bizName: string;
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ type Props = {
|
|||||||
parseInfoOptions: ChatContextType[];
|
parseInfoOptions: ChatContextType[];
|
||||||
parseTip: string;
|
parseTip: string;
|
||||||
currentParseInfo?: ChatContextType;
|
currentParseInfo?: ChatContextType;
|
||||||
|
optionMode?: boolean;
|
||||||
onSelectParseInfo: (parseInfo: ChatContextType) => void;
|
onSelectParseInfo: (parseInfo: ChatContextType) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ const ParseTip: React.FC<Props> = ({
|
|||||||
parseInfoOptions,
|
parseInfoOptions,
|
||||||
parseTip,
|
parseTip,
|
||||||
currentParseInfo,
|
currentParseInfo,
|
||||||
|
optionMode,
|
||||||
onSelectParseInfo,
|
onSelectParseInfo,
|
||||||
}) => {
|
}) => {
|
||||||
const prefixCls = `${PREFIX_CLS}-item`;
|
const prefixCls = `${PREFIX_CLS}-item`;
|
||||||
@@ -38,7 +40,7 @@ const ParseTip: React.FC<Props> = ({
|
|||||||
|
|
||||||
const getTipNode = (parseInfo: ChatContextType, isOptions?: boolean, index?: number) => {
|
const getTipNode = (parseInfo: ChatContextType, isOptions?: boolean, index?: number) => {
|
||||||
const {
|
const {
|
||||||
domainName,
|
modelName,
|
||||||
dateInfo,
|
dateInfo,
|
||||||
dimensionFilters,
|
dimensionFilters,
|
||||||
dimensions,
|
dimensions,
|
||||||
@@ -70,6 +72,7 @@ const ParseTip: React.FC<Props> = ({
|
|||||||
[`${prefixCls}-tip-item-option`]: isOptions,
|
[`${prefixCls}-tip-item-option`]: isOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const entityId = dimensionFilters?.length > 0 ? dimensionFilters[0].value : undefined;
|
||||||
const entityAlias = entity?.alias?.[0]?.split('.')?.[0];
|
const entityAlias = entity?.alias?.[0]?.split('.')?.[0];
|
||||||
const entityName = elementMatches?.find(item => item.element?.type === 'ID')?.element.name;
|
const entityName = elementMatches?.find(item => item.element?.type === 'ID')?.element.name;
|
||||||
|
|
||||||
@@ -106,7 +109,10 @@ const ParseTip: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{queryMode === 'METRIC_ENTITY' || queryMode === 'ENTITY_DETAIL' ? (
|
{queryMode.includes('ENTITY') &&
|
||||||
|
typeof entityId === 'string' &&
|
||||||
|
!!entityAlias &&
|
||||||
|
!!entityName ? (
|
||||||
<div className={`${prefixCls}-tip-item`}>
|
<div className={`${prefixCls}-tip-item`}>
|
||||||
<div className={`${prefixCls}-tip-item-name`}>{entityAlias}:</div>
|
<div className={`${prefixCls}-tip-item-name`}>{entityAlias}:</div>
|
||||||
<div className={itemValueClass}>{entityName}</div>
|
<div className={itemValueClass}>{entityName}</div>
|
||||||
@@ -114,7 +120,7 @@ const ParseTip: React.FC<Props> = ({
|
|||||||
) : (
|
) : (
|
||||||
<div className={`${prefixCls}-tip-item`}>
|
<div className={`${prefixCls}-tip-item`}>
|
||||||
<div className={`${prefixCls}-tip-item-name`}>主题域:</div>
|
<div className={`${prefixCls}-tip-item-name`}>主题域:</div>
|
||||||
<div className={itemValueClass}>{domainName}</div>
|
<div className={itemValueClass}>{modelName}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{modeName === '算指标' && metric && (
|
{modeName === '算指标' && metric && (
|
||||||
@@ -180,10 +186,12 @@ const ParseTip: React.FC<Props> = ({
|
|||||||
|
|
||||||
let tipNode: ReactNode;
|
let tipNode: ReactNode;
|
||||||
|
|
||||||
if (parseInfoOptions.length > 1) {
|
if (parseInfoOptions.length > 1 || optionMode) {
|
||||||
tipNode = (
|
tipNode = (
|
||||||
<div className={`${prefixCls}-multi-options`}>
|
<div className={`${prefixCls}-multi-options`}>
|
||||||
<div>您的问题解析为以下几项,请您点击确认</div>
|
<div>
|
||||||
|
还有以下的相关问题,<strong>请您点击提交</strong>
|
||||||
|
</div>
|
||||||
<div className={`${prefixCls}-options`}>
|
<div className={`${prefixCls}-options`}>
|
||||||
{parseInfoOptions.map((item, index) => getTipNode(item, true, index))}
|
{parseInfoOptions.map((item, index) => getTipNode(item, true, index))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import ExecuteItem from './ExecuteItem';
|
|||||||
type Props = {
|
type Props = {
|
||||||
msg: string;
|
msg: string;
|
||||||
conversationId?: number;
|
conversationId?: number;
|
||||||
domainId?: number;
|
modelId?: number;
|
||||||
filter?: any[];
|
filter?: any[];
|
||||||
isLastMessage?: boolean;
|
isLastMessage?: boolean;
|
||||||
msgData?: MsgDataType;
|
msgData?: MsgDataType;
|
||||||
isMobileMode?: boolean;
|
isMobileMode?: boolean;
|
||||||
triggerResize?: boolean;
|
triggerResize?: boolean;
|
||||||
|
parseOptions?: ChatContextType[];
|
||||||
onMsgDataLoaded?: (data: MsgDataType, valid: boolean) => void;
|
onMsgDataLoaded?: (data: MsgDataType, valid: boolean) => void;
|
||||||
onUpdateMessageScroll?: () => void;
|
onUpdateMessageScroll?: () => void;
|
||||||
};
|
};
|
||||||
@@ -22,19 +23,20 @@ type Props = {
|
|||||||
const ChatItem: React.FC<Props> = ({
|
const ChatItem: React.FC<Props> = ({
|
||||||
msg,
|
msg,
|
||||||
conversationId,
|
conversationId,
|
||||||
domainId,
|
modelId,
|
||||||
filter,
|
filter,
|
||||||
isLastMessage,
|
isLastMessage,
|
||||||
isMobileMode,
|
isMobileMode,
|
||||||
triggerResize,
|
triggerResize,
|
||||||
msgData,
|
msgData,
|
||||||
|
parseOptions,
|
||||||
onMsgDataLoaded,
|
onMsgDataLoaded,
|
||||||
onUpdateMessageScroll,
|
onUpdateMessageScroll,
|
||||||
}) => {
|
}) => {
|
||||||
const [data, setData] = useState<MsgDataType>();
|
const [data, setData] = useState<MsgDataType>();
|
||||||
const [parseLoading, setParseLoading] = useState(false);
|
const [parseLoading, setParseLoading] = useState(false);
|
||||||
const [parseInfo, setParseInfo] = useState<ChatContextType>();
|
const [parseInfo, setParseInfo] = useState<ChatContextType>();
|
||||||
const [parseInfoOptions, setParseInfoOptions] = useState<ChatContextType[]>([]);
|
const [parseInfoOptions, setParseInfoOptions] = useState<ChatContextType[]>(parseOptions || []);
|
||||||
const [parseTip, setParseTip] = useState('');
|
const [parseTip, setParseTip] = useState('');
|
||||||
const [executeLoading, setExecuteLoading] = useState(false);
|
const [executeLoading, setExecuteLoading] = useState(false);
|
||||||
const [executeTip, setExecuteTip] = useState('');
|
const [executeTip, setExecuteTip] = useState('');
|
||||||
@@ -68,20 +70,43 @@ const ChatItem: React.FC<Props> = ({
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onExecute = async (parseInfoValue: ChatContextType, isSwitch?: boolean) => {
|
const onExecute = async (
|
||||||
|
parseInfoValue: ChatContextType,
|
||||||
|
parseInfoOptions?: ChatContextType[]
|
||||||
|
) => {
|
||||||
setExecuteMode(true);
|
setExecuteMode(true);
|
||||||
setExecuteLoading(true);
|
setExecuteLoading(true);
|
||||||
const { data } = await chatExecute(msg, conversationId!, parseInfoValue);
|
const { data } = await chatExecute(msg, conversationId!, parseInfoValue);
|
||||||
setExecuteLoading(false);
|
setExecuteLoading(false);
|
||||||
const valid = updateData(data);
|
const valid = updateData(data);
|
||||||
if (onMsgDataLoaded && !isSwitch) {
|
if (onMsgDataLoaded) {
|
||||||
onMsgDataLoaded({ ...data.data, chatContext: parseInfoValue }, valid);
|
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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSendMsg = async () => {
|
const onSendMsg = async () => {
|
||||||
setParseLoading(true);
|
setParseLoading(true);
|
||||||
const { data: parseData } = await chatParse(msg, conversationId, domainId, filter);
|
const { data: parseData } = await chatParse(msg, conversationId, modelId, filter);
|
||||||
setParseLoading(false);
|
setParseLoading(false);
|
||||||
const { code, data } = parseData || {};
|
const { code, data } = parseData || {};
|
||||||
const { state, selectedParses } = data || {};
|
const { state, selectedParses } = data || {};
|
||||||
@@ -91,7 +116,7 @@ const ChatItem: React.FC<Props> = ({
|
|||||||
selectedParses == null ||
|
selectedParses == null ||
|
||||||
selectedParses.length === 0 ||
|
selectedParses.length === 0 ||
|
||||||
(selectedParses.length === 1 &&
|
(selectedParses.length === 1 &&
|
||||||
!selectedParses[0]?.domainName &&
|
!selectedParses[0]?.modelName &&
|
||||||
!selectedParses[0]?.properties?.CONTEXT?.plugin?.name &&
|
!selectedParses[0]?.properties?.CONTEXT?.plugin?.name &&
|
||||||
selectedParses[0]?.queryMode !== 'WEB_PAGE')
|
selectedParses[0]?.queryMode !== 'WEB_PAGE')
|
||||||
) {
|
) {
|
||||||
@@ -102,15 +127,13 @@ const ChatItem: React.FC<Props> = ({
|
|||||||
onUpdateMessageScroll();
|
onUpdateMessageScroll();
|
||||||
}
|
}
|
||||||
setParseInfoOptions(selectedParses || []);
|
setParseInfoOptions(selectedParses || []);
|
||||||
if (selectedParses.length === 1) {
|
const parseInfoValue = selectedParses[0];
|
||||||
const parseInfoValue = selectedParses[0];
|
setParseInfo(parseInfoValue);
|
||||||
setParseInfo(parseInfoValue);
|
onExecute(parseInfoValue, selectedParses);
|
||||||
onExecute(parseInfoValue);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data !== undefined) {
|
if (data !== undefined || parseOptions !== undefined || executeTip !== '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msgData) {
|
if (msgData) {
|
||||||
@@ -124,7 +147,7 @@ const ChatItem: React.FC<Props> = ({
|
|||||||
|
|
||||||
const onSwitchEntity = async (entityId: string) => {
|
const onSwitchEntity = async (entityId: string) => {
|
||||||
setEntitySwitching(true);
|
setEntitySwitching(true);
|
||||||
const res = await switchEntity(entityId, data?.chatContext?.domainId, conversationId || 0);
|
const res = await switchEntity(entityId, data?.chatContext?.modelId, conversationId || 0);
|
||||||
setEntitySwitching(false);
|
setEntitySwitching(false);
|
||||||
setData(res.data.data);
|
setData(res.data.data);
|
||||||
};
|
};
|
||||||
@@ -135,7 +158,7 @@ const ChatItem: React.FC<Props> = ({
|
|||||||
|
|
||||||
const onSelectParseInfo = async (parseInfoValue: ChatContextType) => {
|
const onSelectParseInfo = async (parseInfoValue: ChatContextType) => {
|
||||||
setParseInfo(parseInfoValue);
|
setParseInfo(parseInfoValue);
|
||||||
onExecute(parseInfoValue, parseInfo !== undefined);
|
onExecute(parseInfoValue);
|
||||||
if (onUpdateMessageScroll) {
|
if (onUpdateMessageScroll) {
|
||||||
onUpdateMessageScroll();
|
onUpdateMessageScroll();
|
||||||
}
|
}
|
||||||
@@ -148,9 +171,10 @@ const ChatItem: React.FC<Props> = ({
|
|||||||
<div className={`${prefixCls}-content`}>
|
<div className={`${prefixCls}-content`}>
|
||||||
<ParseTip
|
<ParseTip
|
||||||
parseLoading={parseLoading}
|
parseLoading={parseLoading}
|
||||||
parseInfoOptions={parseInfoOptions}
|
parseInfoOptions={parseOptions || parseInfoOptions.slice(0, 1)}
|
||||||
parseTip={parseTip}
|
parseTip={parseTip}
|
||||||
currentParseInfo={parseInfo}
|
currentParseInfo={parseInfo}
|
||||||
|
optionMode={parseOptions !== undefined}
|
||||||
onSelectParseInfo={onSelectParseInfo}
|
onSelectParseInfo={onSelectParseInfo}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { PREFIX_CLS } from '../../../common/constants';
|
import { PREFIX_CLS } from '../../../common/constants';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
domain: string;
|
model: string;
|
||||||
onApplyAuth?: (domain: string) => void;
|
onApplyAuth?: (model: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ApplyAuth: React.FC<Props> = ({ domain, onApplyAuth }) => {
|
const ApplyAuth: React.FC<Props> = ({ model, onApplyAuth }) => {
|
||||||
const prefixCls = `${PREFIX_CLS}-apply-auth`;
|
const prefixCls = `${PREFIX_CLS}-apply-auth`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -15,7 +15,7 @@ const ApplyAuth: React.FC<Props> = ({ domain, onApplyAuth }) => {
|
|||||||
<span
|
<span
|
||||||
className={`${prefixCls}-apply`}
|
className={`${prefixCls}-apply`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onApplyAuth(domain);
|
onApplyAuth(model);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
点击申请
|
点击申请
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type Props = {
|
|||||||
drillDownDimension?: DrillDownDimensionType;
|
drillDownDimension?: DrillDownDimensionType;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onSelectDimension: (dimension?: DrillDownDimensionType) => void;
|
onSelectDimension: (dimension?: DrillDownDimensionType) => void;
|
||||||
onApplyAuth?: (domain: string) => void;
|
onApplyAuth?: (model: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const BarChart: React.FC<Props> = ({
|
const BarChart: React.FC<Props> = ({
|
||||||
@@ -152,7 +152,7 @@ const BarChart: React.FC<Props> = ({
|
|||||||
if (metricColumn && !metricColumn?.authorized) {
|
if (metricColumn && !metricColumn?.authorized) {
|
||||||
return (
|
return (
|
||||||
<NoPermissionChart
|
<NoPermissionChart
|
||||||
domain={entityInfo?.domainInfo.name || ''}
|
model={entityInfo?.modelInfo.name || ''}
|
||||||
chartType="barChart"
|
chartType="barChart"
|
||||||
onApplyAuth={onApplyAuth}
|
onApplyAuth={onApplyAuth}
|
||||||
/>
|
/>
|
||||||
@@ -193,11 +193,9 @@ const BarChart: React.FC<Props> = ({
|
|||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<div className={`${prefixCls}-chart`} ref={chartRef} />
|
<div className={`${prefixCls}-chart`} ref={chartRef} />
|
||||||
</Spin>
|
</Spin>
|
||||||
{(queryMode === 'METRIC_DOMAIN' ||
|
{queryMode.includes('METRIC') && (
|
||||||
queryMode === 'METRIC_FILTER' ||
|
|
||||||
queryMode === 'METRIC_GROUPBY') && (
|
|
||||||
<DrillDownDimensions
|
<DrillDownDimensions
|
||||||
domainId={chatContext.domainId}
|
modelId={chatContext.modelId}
|
||||||
drillDownDimension={drillDownDimension}
|
drillDownDimension={drillDownDimension}
|
||||||
dimensionFilters={chatContext.dimensionFilters}
|
dimensionFilters={chatContext.dimensionFilters}
|
||||||
onSelectDimension={onSelectDimension}
|
onSelectDimension={onSelectDimension}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const Message: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const prefixCls = `${PREFIX_CLS}-message`;
|
const prefixCls = `${PREFIX_CLS}-message`;
|
||||||
|
|
||||||
const { domainName, dateInfo, dimensionFilters } = chatContext || {};
|
const { modelName, dateInfo, dimensionFilters } = chatContext || {};
|
||||||
const { startDate, endDate } = dateInfo || {};
|
const { startDate, endDate } = dateInfo || {};
|
||||||
|
|
||||||
const entityInfoList =
|
const entityInfoList =
|
||||||
@@ -67,7 +67,7 @@ const Message: React.FC<Props> = ({
|
|||||||
<div className={`${prefixCls}-main-entity-info`}>
|
<div className={`${prefixCls}-main-entity-info`}>
|
||||||
<div className={`${prefixCls}-info-item`}>
|
<div className={`${prefixCls}-info-item`}>
|
||||||
<div className={`${prefixCls}-info-name`}>主题域:</div>
|
<div className={`${prefixCls}-info-name`}>主题域:</div>
|
||||||
<div className={`${prefixCls}-info-value`}>{domainName}</div>
|
<div className={`${prefixCls}-info-value`}>{modelName}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`${prefixCls}-info-item`}>
|
<div className={`${prefixCls}-info-item`}>
|
||||||
<div className={`${prefixCls}-info-name`}>时间:</div>
|
<div className={`${prefixCls}-info-name`}>时间:</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-domain-name {
|
&-model-name {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ type Props = {
|
|||||||
drillDownDimension?: DrillDownDimensionType;
|
drillDownDimension?: DrillDownDimensionType;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onSelectDimension: (dimension?: DrillDownDimensionType) => void;
|
onSelectDimension: (dimension?: DrillDownDimensionType) => void;
|
||||||
onApplyAuth?: (domain: string) => void;
|
onApplyAuth?: (model: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MetricCard: React.FC<Props> = ({
|
const MetricCard: React.FC<Props> = ({
|
||||||
@@ -64,7 +64,7 @@ const MetricCard: React.FC<Props> = ({
|
|||||||
<div className={indicatorClass}>
|
<div className={indicatorClass}>
|
||||||
<div className={`${prefixCls}-date-range`}>{startDate}</div>
|
<div className={`${prefixCls}-date-range`}>{startDate}</div>
|
||||||
{indicatorColumn && !indicatorColumn?.authorized ? (
|
{indicatorColumn && !indicatorColumn?.authorized ? (
|
||||||
<ApplyAuth domain={entityInfo?.domainInfo.name || ''} onApplyAuth={onApplyAuth} />
|
<ApplyAuth model={entityInfo?.modelInfo.name || ''} onApplyAuth={onApplyAuth} />
|
||||||
) : (
|
) : (
|
||||||
<div className={`${prefixCls}-indicator-value`}>
|
<div className={`${prefixCls}-indicator-value`}>
|
||||||
{formatMetric(queryResults?.[0]?.[indicatorColumnName]) || '-'}
|
{formatMetric(queryResults?.[0]?.[indicatorColumnName]) || '-'}
|
||||||
@@ -79,10 +79,10 @@ const MetricCard: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Spin>
|
</Spin>
|
||||||
{(queryMode === 'METRIC_DOMAIN' || queryMode === 'METRIC_FILTER') && (
|
{queryMode.includes('METRIC') && (
|
||||||
<div className={`${prefixCls}-drill-down-dimensions`}>
|
<div className={`${prefixCls}-drill-down-dimensions`}>
|
||||||
<DrillDownDimensions
|
<DrillDownDimensions
|
||||||
domainId={chatContext.domainId}
|
modelId={chatContext.modelId}
|
||||||
dimensionFilters={chatContext.dimensionFilters}
|
dimensionFilters={chatContext.dimensionFilters}
|
||||||
drillDownDimension={drillDownDimension}
|
drillDownDimension={drillDownDimension}
|
||||||
onSelectDimension={onSelectDimension}
|
onSelectDimension={onSelectDimension}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@
|
|||||||
|
|
||||||
&-drill-down-dimensions {
|
&-drill-down-dimensions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -44px;
|
bottom: -38px;
|
||||||
left: -16;
|
left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,17 +14,17 @@ import { ColumnType } from '../../../common/type';
|
|||||||
import NoPermissionChart from '../NoPermissionChart';
|
import NoPermissionChart from '../NoPermissionChart';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
domain?: string;
|
model?: string;
|
||||||
dateColumnName: string;
|
dateColumnName: string;
|
||||||
categoryColumnName: string;
|
categoryColumnName: string;
|
||||||
metricField: ColumnType;
|
metricField: ColumnType;
|
||||||
resultList: any[];
|
resultList: any[];
|
||||||
triggerResize?: boolean;
|
triggerResize?: boolean;
|
||||||
onApplyAuth?: (domain: string) => void;
|
onApplyAuth?: (model: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MetricTrendChart: React.FC<Props> = ({
|
const MetricTrendChart: React.FC<Props> = ({
|
||||||
domain,
|
model,
|
||||||
dateColumnName,
|
dateColumnName,
|
||||||
categoryColumnName,
|
categoryColumnName,
|
||||||
metricField,
|
metricField,
|
||||||
@@ -204,7 +204,7 @@ const MetricTrendChart: React.FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{!metricField.authorized ? (
|
{!metricField.authorized ? (
|
||||||
<NoPermissionChart domain={domain || ''} onApplyAuth={onApplyAuth} />
|
<NoPermissionChart model={model || ''} onApplyAuth={onApplyAuth} />
|
||||||
) : (
|
) : (
|
||||||
<div className={`${prefixCls}-flow-trend-chart`} ref={chartRef} />
|
<div className={`${prefixCls}-flow-trend-chart`} ref={chartRef} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type Props = {
|
|||||||
data: MsgDataType;
|
data: MsgDataType;
|
||||||
chartIndex: number;
|
chartIndex: number;
|
||||||
triggerResize?: boolean;
|
triggerResize?: boolean;
|
||||||
onApplyAuth?: (domain: string) => void;
|
onApplyAuth?: (model: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApplyAuth }) => {
|
const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApplyAuth }) => {
|
||||||
@@ -36,6 +36,7 @@ const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApply
|
|||||||
const [currentDateOption, setCurrentDateOption] = useState<number>(initialDateOption);
|
const [currentDateOption, setCurrentDateOption] = useState<number>(initialDateOption);
|
||||||
const [dimensions, setDimensions] = useState<FieldType[]>(chatContext?.dimensions);
|
const [dimensions, setDimensions] = useState<FieldType[]>(chatContext?.dimensions);
|
||||||
const [drillDownDimension, setDrillDownDimension] = useState<DrillDownDimensionType>();
|
const [drillDownDimension, setDrillDownDimension] = useState<DrillDownDimensionType>();
|
||||||
|
const [aggregateInfoValue, setAggregateInfoValue] = useState<any>(aggregateInfo);
|
||||||
const [dateModeValue, setDateModeValue] = useState(dateMode);
|
const [dateModeValue, setDateModeValue] = useState(dateMode);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@@ -72,6 +73,7 @@ const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApply
|
|||||||
if (data.code === 200) {
|
if (data.code === 200) {
|
||||||
setColumns(data.data?.queryColumns || []);
|
setColumns(data.data?.queryColumns || []);
|
||||||
setDataSource(data.data?.queryResults || []);
|
setDataSource(data.data?.queryResults || []);
|
||||||
|
setAggregateInfoValue(data.data?.aggregateInfo);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -172,7 +174,9 @@ const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApply
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{aggregateInfo?.metricInfos?.length > 0 && <MetricInfo aggregateInfo={aggregateInfo} />}
|
{aggregateInfoValue?.metricInfos?.length > 0 && (
|
||||||
|
<MetricInfo aggregateInfo={aggregateInfoValue} />
|
||||||
|
)}
|
||||||
<div className={`${prefixCls}-date-options`}>
|
<div className={`${prefixCls}-date-options`}>
|
||||||
{dateOptions.map((dateOption: { label: string; value: number }, index: number) => {
|
{dateOptions.map((dateOption: { label: string; value: number }, index: number) => {
|
||||||
const dateOptionClass = classNames(`${prefixCls}-date-option`, {
|
const dateOptionClass = classNames(`${prefixCls}-date-option`, {
|
||||||
@@ -205,7 +209,7 @@ const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApply
|
|||||||
<Table data={{ ...data, queryResults: dataSource }} onApplyAuth={onApplyAuth} />
|
<Table data={{ ...data, queryResults: dataSource }} onApplyAuth={onApplyAuth} />
|
||||||
) : (
|
) : (
|
||||||
<MetricTrendChart
|
<MetricTrendChart
|
||||||
domain={entityInfo?.domainInfo.name}
|
model={entityInfo?.modelInfo.name}
|
||||||
dateColumnName={dateColumnName}
|
dateColumnName={dateColumnName}
|
||||||
categoryColumnName={categoryColumnName}
|
categoryColumnName={categoryColumnName}
|
||||||
metricField={currentMetricField}
|
metricField={currentMetricField}
|
||||||
@@ -215,11 +219,9 @@ const MetricTrend: React.FC<Props> = ({ data, chartIndex, triggerResize, onApply
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Spin>
|
</Spin>
|
||||||
{(queryMode === 'METRIC_DOMAIN' ||
|
{queryMode.includes('METRIC') && (
|
||||||
queryMode === 'METRIC_FILTER' ||
|
|
||||||
queryMode === 'METRIC_GROUPBY') && (
|
|
||||||
<DrillDownDimensions
|
<DrillDownDimensions
|
||||||
domainId={chatContext.domainId}
|
modelId={chatContext.modelId}
|
||||||
drillDownDimension={drillDownDimension}
|
drillDownDimension={drillDownDimension}
|
||||||
dimensionFilters={chatContext.dimensionFilters}
|
dimensionFilters={chatContext.dimensionFilters}
|
||||||
onSelectDimension={onSelectDimension}
|
onSelectDimension={onSelectDimension}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import { CLS_PREFIX } from '../../../common/constants';
|
|||||||
import ApplyAuth from '../ApplyAuth';
|
import ApplyAuth from '../ApplyAuth';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
domain: string;
|
model: string;
|
||||||
chartType?: string;
|
chartType?: string;
|
||||||
onApplyAuth?: (domain: string) => void;
|
onApplyAuth?: (model: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NoPermissionChart: React.FC<Props> = ({ domain, chartType, onApplyAuth }) => {
|
const NoPermissionChart: React.FC<Props> = ({ model, chartType, onApplyAuth }) => {
|
||||||
const prefixCls = `${CLS_PREFIX}-no-permission-chart`;
|
const prefixCls = `${CLS_PREFIX}-no-permission-chart`;
|
||||||
|
|
||||||
const chartHolderClass = classNames(`${prefixCls}-holder`, {
|
const chartHolderClass = classNames(`${prefixCls}-holder`, {
|
||||||
@@ -19,7 +19,7 @@ const NoPermissionChart: React.FC<Props> = ({ domain, chartType, onApplyAuth })
|
|||||||
<div className={prefixCls}>
|
<div className={prefixCls}>
|
||||||
<div className={chartHolderClass} />
|
<div className={chartHolderClass} />
|
||||||
<div className={`${prefixCls}-no-permission`}>
|
<div className={`${prefixCls}-no-permission`}>
|
||||||
<ApplyAuth domain={domain} onApplyAuth={onApplyAuth} />
|
<ApplyAuth model={model} onApplyAuth={onApplyAuth} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { SizeType } from 'antd/es/config-provider/SizeContext';
|
|||||||
type Props = {
|
type Props = {
|
||||||
data: MsgDataType;
|
data: MsgDataType;
|
||||||
size?: SizeType;
|
size?: SizeType;
|
||||||
onApplyAuth?: (domain: string) => void;
|
onApplyAuth?: (model: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Table: React.FC<Props> = ({ data, size, onApplyAuth }) => {
|
const Table: React.FC<Props> = ({ data, size, onApplyAuth }) => {
|
||||||
@@ -24,9 +24,7 @@ const Table: React.FC<Props> = ({ data, size, onApplyAuth }) => {
|
|||||||
title: name || nameEn,
|
title: name || nameEn,
|
||||||
render: (value: string | number) => {
|
render: (value: string | number) => {
|
||||||
if (!authorized) {
|
if (!authorized) {
|
||||||
return (
|
return <ApplyAuth model={entityInfo?.modelInfo.name || ''} onApplyAuth={onApplyAuth} />;
|
||||||
<ApplyAuth domain={entityInfo?.domainInfo.name || ''} onApplyAuth={onApplyAuth} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (dataFormatType === 'percent') {
|
if (dataFormatType === 'percent') {
|
||||||
return (
|
return (
|
||||||
@@ -71,7 +69,7 @@ const Table: React.FC<Props> = ({ data, size, onApplyAuth }) => {
|
|||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
dataSource={queryResults}
|
dataSource={queryResults}
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
scroll={{ x: 'max-content' }}
|
// scroll={{ x: 'max-content' }}
|
||||||
rowClassName={getRowClassName}
|
rowClassName={getRowClassName}
|
||||||
size={size}
|
size={size}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
@table-prefix-cls: ~'@{supersonic-chat-prefix}-table';
|
@table-prefix-cls: ~'@{supersonic-chat-prefix}-table';
|
||||||
|
|
||||||
.@{table-prefix-cls} {
|
.@{table-prefix-cls} {
|
||||||
margin-top: 20px;
|
margin-top: 16px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
&-photo {
|
&-photo {
|
||||||
|
|||||||
@@ -35,10 +35,15 @@ const ChatMsg: React.FC<Props> = ({ question, data, chartIndex, isMobileMode, tr
|
|||||||
const metricFields = columns.filter(item => item.showType === 'NUMBER');
|
const metricFields = columns.filter(item => item.showType === 'NUMBER');
|
||||||
|
|
||||||
const isMetricCard =
|
const isMetricCard =
|
||||||
(queryMode === 'METRIC_DOMAIN' || queryMode === 'METRIC_FILTER') &&
|
queryMode.includes('METRIC') &&
|
||||||
(singleData || chatContext?.dateInfo?.startDate === chatContext?.dateInfo?.endDate);
|
(singleData || chatContext?.dateInfo?.startDate === chatContext?.dateInfo?.endDate);
|
||||||
|
|
||||||
const isText = columns.length === 1 && columns[0].showType === 'CATEGORY' && singleData;
|
const isText =
|
||||||
|
columns.length === 1 &&
|
||||||
|
columns[0].showType === 'CATEGORY' &&
|
||||||
|
!queryMode.includes('METRIC') &&
|
||||||
|
!queryMode.includes('ENTITY') &&
|
||||||
|
singleData;
|
||||||
|
|
||||||
const onLoadData = async (value: any) => {
|
const onLoadData = async (value: any) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { DownOutlined } from '@ant-design/icons';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
domainId: number;
|
modelId: number;
|
||||||
drillDownDimension?: DrillDownDimensionType;
|
drillDownDimension?: DrillDownDimensionType;
|
||||||
isMetricCard?: boolean;
|
isMetricCard?: boolean;
|
||||||
dimensionFilters?: FilterItemType[];
|
dimensionFilters?: FilterItemType[];
|
||||||
@@ -17,7 +17,7 @@ type Props = {
|
|||||||
const MAX_DIMENSION_COUNT = 20;
|
const MAX_DIMENSION_COUNT = 20;
|
||||||
|
|
||||||
const DrillDownDimensions: React.FC<Props> = ({
|
const DrillDownDimensions: React.FC<Props> = ({
|
||||||
domainId,
|
modelId,
|
||||||
drillDownDimension,
|
drillDownDimension,
|
||||||
isMetricCard,
|
isMetricCard,
|
||||||
dimensionFilters,
|
dimensionFilters,
|
||||||
@@ -30,7 +30,7 @@ const DrillDownDimensions: React.FC<Props> = ({
|
|||||||
const prefixCls = `${CLS_PREFIX}-drill-down-dimensions`;
|
const prefixCls = `${CLS_PREFIX}-drill-down-dimensions`;
|
||||||
|
|
||||||
const initData = async () => {
|
const initData = async () => {
|
||||||
const res = await queryDrillDownDimensions(domainId);
|
const res = await queryDrillDownDimensions(modelId);
|
||||||
setDimensions(
|
setDimensions(
|
||||||
res.data.data.dimensions
|
res.data.data.dimensions
|
||||||
.filter(dimension => !dimensionFilters?.some(filter => filter.name === dimension.name))
|
.filter(dimension => !dimensionFilters?.some(filter => filter.name === dimension.name))
|
||||||
|
|||||||
@@ -10,16 +10,16 @@ import classNames from 'classnames';
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
entityId: string | number;
|
entityId: string | number;
|
||||||
domainId: number;
|
modelId: number;
|
||||||
domainName: string;
|
modelName: string;
|
||||||
isMobileMode?: boolean;
|
isMobileMode?: boolean;
|
||||||
onSelect: (option: string) => void;
|
onSelect: (option: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const RecommendOptions: React.FC<Props> = ({
|
const RecommendOptions: React.FC<Props> = ({
|
||||||
entityId,
|
entityId,
|
||||||
domainId,
|
modelId,
|
||||||
domainName,
|
modelName,
|
||||||
isMobileMode,
|
isMobileMode,
|
||||||
onSelect,
|
onSelect,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -30,7 +30,7 @@ const RecommendOptions: React.FC<Props> = ({
|
|||||||
|
|
||||||
const initData = async () => {
|
const initData = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const res = await queryEntities(entityId, domainId);
|
const res = await queryEntities(entityId, modelId);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setData(res.data.data);
|
setData(res.data.data);
|
||||||
};
|
};
|
||||||
@@ -51,7 +51,7 @@ const RecommendOptions: React.FC<Props> = ({
|
|||||||
<div className={`${prefixCls}-item-name-column`}>
|
<div className={`${prefixCls}-item-name-column`}>
|
||||||
<Avatar
|
<Avatar
|
||||||
shape="square"
|
shape="square"
|
||||||
icon={<IconFont type={domainName === '艺人库' ? 'icon-geshou' : 'icon-zhuanji'} />}
|
icon={<IconFont type={modelName === '艺人库' ? 'icon-geshou' : 'icon-zhuanji'} />}
|
||||||
src={record.url}
|
src={record.url}
|
||||||
/>
|
/>
|
||||||
<div className={`${prefixCls}-entity-name`}>
|
<div className={`${prefixCls}-entity-name`}>
|
||||||
@@ -64,7 +64,7 @@ const RecommendOptions: React.FC<Props> = ({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const playCntColumnIdex = domainName.includes('歌曲')
|
const playCntColumnIdex = modelName.includes('歌曲')
|
||||||
? 'tme3platAvgLogYyPlayCnt'
|
? 'tme3platAvgLogYyPlayCnt'
|
||||||
: 'tme3platJsPlayCnt';
|
: 'tme3platJsPlayCnt';
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ const RecommendOptions: React.FC<Props> = ({
|
|||||||
? [basicColumn]
|
? [basicColumn]
|
||||||
: [
|
: [
|
||||||
basicColumn,
|
basicColumn,
|
||||||
domainName.includes('艺人')
|
modelName.includes('艺人')
|
||||||
? {
|
? {
|
||||||
dataIndex: 'onlineSongCnt',
|
dataIndex: 'onlineSongCnt',
|
||||||
key: 'onlineSongCnt',
|
key: 'onlineSongCnt',
|
||||||
@@ -95,7 +95,7 @@ const RecommendOptions: React.FC<Props> = ({
|
|||||||
dataIndex: playCntColumnIdex,
|
dataIndex: playCntColumnIdex,
|
||||||
key: playCntColumnIdex,
|
key: playCntColumnIdex,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
title: domainName.includes('歌曲') ? '近7天日均运营播放量' : '昨日结算播放量',
|
title: modelName.includes('歌曲') ? '近7天日均运营播放量' : '昨日结算播放量',
|
||||||
render: (value: string) => {
|
render: (value: string) => {
|
||||||
return value ? getFormattedValue(+value) : '-';
|
return value ? getFormattedValue(+value) : '-';
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,21 +26,22 @@ const Tools: React.FC<Props> = ({
|
|||||||
onChangeChart,
|
onChangeChart,
|
||||||
}) => {
|
}) => {
|
||||||
const [recommendOptionsOpen, setRecommendOptionsOpen] = useState(false);
|
const [recommendOptionsOpen, setRecommendOptionsOpen] = useState(false);
|
||||||
const { queryColumns, queryResults, queryId, chatContext, queryMode } = data || {};
|
const { queryColumns, queryResults, queryId, chatContext, queryMode, entityInfo } = data || {};
|
||||||
const [score, setScore] = useState(scoreValue || 0);
|
const [score, setScore] = useState(scoreValue || 0);
|
||||||
|
|
||||||
const prefixCls = `${CLS_PREFIX}-tools`;
|
const prefixCls = `${CLS_PREFIX}-tools`;
|
||||||
|
|
||||||
|
const singleData = queryResults.length === 1;
|
||||||
|
const isMetricCard =
|
||||||
|
queryMode.includes('METRIC') &&
|
||||||
|
(singleData || chatContext?.dateInfo?.startDate === chatContext?.dateInfo?.endDate);
|
||||||
|
|
||||||
const noDashboard =
|
const noDashboard =
|
||||||
(queryColumns?.length === 1 &&
|
(queryColumns?.length === 1 &&
|
||||||
queryColumns[0].showType === 'CATEGORY' &&
|
queryColumns[0].showType === 'CATEGORY' &&
|
||||||
queryResults?.length === 1) ||
|
queryResults?.length === 1) ||
|
||||||
(!queryMode.includes('METRIC') && !queryMode.includes('ENTITY'));
|
(!queryMode.includes('METRIC') && !queryMode.includes('ENTITY')) ||
|
||||||
|
isMetricCard;
|
||||||
console.log(
|
|
||||||
'chatContext?.properties?.CONTEXT?.plugin?.name',
|
|
||||||
chatContext?.properties?.CONTEXT?.plugin?.name
|
|
||||||
);
|
|
||||||
|
|
||||||
const changeChart = () => {
|
const changeChart = () => {
|
||||||
onChangeChart();
|
onChangeChart();
|
||||||
@@ -74,13 +75,13 @@ const Tools: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={prefixCls}>
|
<div className={prefixCls}>
|
||||||
{/* {isLastMessage && chatContext?.domainId && entityInfo?.entityId && (
|
{/* {isLastMessage && chatContext?.modelId && entityInfo?.entityId && (
|
||||||
<Popover
|
<Popover
|
||||||
content={
|
content={
|
||||||
<RecommendOptions
|
<RecommendOptions
|
||||||
entityId={entityInfo.entityId}
|
entityId={entityInfo.entityId}
|
||||||
domainId={chatContext.domainId}
|
modelId={chatContext.modelId}
|
||||||
domainName={chatContext.domainName}
|
modelName={chatContext.modelName}
|
||||||
isMobileMode={isMobileMode}
|
isMobileMode={isMobileMode}
|
||||||
onSelect={switchEntity}
|
onSelect={switchEntity}
|
||||||
/>
|
/>
|
||||||
@@ -105,7 +106,7 @@ const Tools: React.FC<Props> = ({
|
|||||||
加入看板
|
加入看板
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{isLastMessage && (
|
{isLastMessage && !isMetricCard && (
|
||||||
<div className={`${prefixCls}-feedback`}>
|
<div className={`${prefixCls}-feedback`}>
|
||||||
<div>这个回答正确吗?</div>
|
<div>这个回答正确吗?</div>
|
||||||
<LikeOutlined className={likeClass} onClick={like} />
|
<LikeOutlined className={likeClass} onClick={like} />
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ const Chat = () => {
|
|||||||
msg={msg}
|
msg={msg}
|
||||||
// msgData={data}
|
// msgData={data}
|
||||||
onMsgDataLoaded={onMsgDataLoaded}
|
onMsgDataLoaded={onMsgDataLoaded}
|
||||||
domainId={37}
|
|
||||||
isLastMessage
|
isLastMessage
|
||||||
isMobileMode
|
isMobileMode
|
||||||
triggerResize={triggerResize}
|
triggerResize={triggerResize}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export { default as ChatItem } from './components/ChatItem';
|
|||||||
export type {
|
export type {
|
||||||
SearchRecommendItem,
|
SearchRecommendItem,
|
||||||
FieldType,
|
FieldType,
|
||||||
DomainInfoType,
|
ModelInfoType,
|
||||||
EntityInfoType,
|
EntityInfoType,
|
||||||
DateInfoType,
|
DateInfoType,
|
||||||
ChatContextType,
|
ChatContextType,
|
||||||
|
|||||||
@@ -6,30 +6,30 @@ const DEFAULT_CHAT_ID = 0;
|
|||||||
|
|
||||||
const prefix = '/api';
|
const prefix = '/api';
|
||||||
|
|
||||||
export function searchRecommend(queryText: string, chatId?: number, domainId?: number) {
|
export function searchRecommend(queryText: string, chatId?: number, modelId?: number) {
|
||||||
return axios.post<Result<SearchRecommendItem[]>>(`${prefix}/chat/query/search`, {
|
return axios.post<Result<SearchRecommendItem[]>>(`${prefix}/chat/query/search`, {
|
||||||
queryText,
|
queryText,
|
||||||
chatId: chatId || DEFAULT_CHAT_ID,
|
chatId: chatId || DEFAULT_CHAT_ID,
|
||||||
domainId,
|
modelId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function chatQuery(queryText: string, chatId?: number, domainId?: number, filters?: any[]) {
|
export function chatQuery(queryText: string, chatId?: number, modelId?: number, filters?: any[]) {
|
||||||
return axios.post<Result<MsgDataType>>(`${prefix}/chat/query/query`, {
|
return axios.post<Result<MsgDataType>>(`${prefix}/chat/query/query`, {
|
||||||
queryText,
|
queryText,
|
||||||
chatId: chatId || DEFAULT_CHAT_ID,
|
chatId: chatId || DEFAULT_CHAT_ID,
|
||||||
domainId,
|
modelId,
|
||||||
queryFilters: filters ? {
|
queryFilters: filters ? {
|
||||||
filters
|
filters
|
||||||
} : undefined,
|
} : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function chatParse(queryText: string, chatId?: number, domainId?: number, filters?: any[]) {
|
export function chatParse(queryText: string, chatId?: number, modelId?: number, filters?: any[]) {
|
||||||
return axios.post<Result<ParseDataType>>(`${prefix}/chat/query/parse`, {
|
return axios.post<Result<ParseDataType>>(`${prefix}/chat/query/parse`, {
|
||||||
queryText,
|
queryText,
|
||||||
chatId: chatId || DEFAULT_CHAT_ID,
|
chatId: chatId || DEFAULT_CHAT_ID,
|
||||||
domainId,
|
modelId,
|
||||||
queryFilters: filters ? {
|
queryFilters: filters ? {
|
||||||
filters
|
filters
|
||||||
} : undefined,
|
} : undefined,
|
||||||
@@ -44,10 +44,10 @@ export function chatExecute(queryText: string, chatId: number, parseInfo: ChatC
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function switchEntity(entityId: string, domainId?: number, chatId?: number) {
|
export function switchEntity(entityId: string, modelId?: number, chatId?: number) {
|
||||||
return axios.post<Result<any>>(`${prefix}/chat/query/switchQuery`, {
|
return axios.post<Result<any>>(`${prefix}/chat/query/switchQuery`, {
|
||||||
queryText: entityId,
|
queryText: entityId,
|
||||||
domainId,
|
modelId,
|
||||||
chatId: chatId || DEFAULT_CHAT_ID,
|
chatId: chatId || DEFAULT_CHAT_ID,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -63,8 +63,8 @@ export function queryContext(queryText: string, chatId?: number) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function querySuggestionInfo(domainId: number) {
|
export function querySuggestionInfo(modelId: number) {
|
||||||
return axios.get<Result<any>>(`${prefix}/chat/recommend/${domainId}`);
|
return axios.get<Result<any>>(`${prefix}/chat/recommend/${modelId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHistoryMsg(current: number, chatId: number = DEFAULT_CHAT_ID, pageSize: number = 10) {
|
export function getHistoryMsg(current: number, chatId: number = DEFAULT_CHAT_ID, pageSize: number = 10) {
|
||||||
@@ -98,10 +98,10 @@ export function getAllConversations() {
|
|||||||
return axios.get<Result<any>>(`${prefix}/chat/manage/getAll`);
|
return axios.get<Result<any>>(`${prefix}/chat/manage/getAll`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function queryEntities(entityId: string | number, domainId: number) {
|
export function queryEntities(entityId: string | number, modelId: number) {
|
||||||
return axios.post<Result<any>>(`${prefix}/chat/query/choice`, {
|
return axios.post<Result<any>>(`${prefix}/chat/query/choice`, {
|
||||||
entityId,
|
entityId,
|
||||||
domainId,
|
modelId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +109,6 @@ export function updateQAFeedback(questionId: number, score: number) {
|
|||||||
return axios.post<Result<any>>(`${prefix}/chat/manage/updateQAFeedback?id=${questionId}&score=${score}&feedback=`);
|
return axios.post<Result<any>>(`${prefix}/chat/manage/updateQAFeedback?id=${questionId}&score=${score}&feedback=`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function queryDrillDownDimensions(domainId: number) {
|
export function queryDrillDownDimensions(modelId: number) {
|
||||||
return axios.get<Result<{ dimensions: DrillDownDimensionType[] }>>(`${prefix}/chat/recommend/metric/${domainId}`);
|
return axios.get<Result<{ dimensions: DrillDownDimensionType[] }>>(`${prefix}/chat/recommend/metric/${modelId}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { queryToken } from './services/login';
|
|||||||
import { queryCurrentUser } from './services/user';
|
import { queryCurrentUser } from './services/user';
|
||||||
import { traverseRoutes, deleteUrlQuery } from './utils/utils';
|
import { traverseRoutes, deleteUrlQuery } from './utils/utils';
|
||||||
import { publicPath } from '../config/defaultSettings';
|
import { publicPath } from '../config/defaultSettings';
|
||||||
|
import Copilot from './pages/Copilot';
|
||||||
export { request } from './services/request';
|
export { request } from './services/request';
|
||||||
|
|
||||||
const TOKEN_KEY = AUTH_TOKEN_KEY;
|
const TOKEN_KEY = AUTH_TOKEN_KEY;
|
||||||
@@ -148,7 +149,12 @@ export const layout: RunTimeLayoutConfig = (params) => {
|
|||||||
disableContentMargin: true,
|
disableContentMargin: true,
|
||||||
menuHeaderRender: undefined,
|
menuHeaderRender: undefined,
|
||||||
childrenRender: (dom) => {
|
childrenRender: (dom) => {
|
||||||
return dom;
|
return (
|
||||||
|
<>
|
||||||
|
{dom}
|
||||||
|
{history.location.pathname !== '/chat' && <Copilot />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
openKeys: false,
|
openKeys: false,
|
||||||
...initialState?.settings,
|
...initialState?.settings,
|
||||||
|
|||||||
@@ -9,21 +9,21 @@ import { searchRecommend } from 'supersonic-chat-sdk';
|
|||||||
import { SemanticTypeEnum, SEMANTIC_TYPE_MAP } from '../constants';
|
import { SemanticTypeEnum, SEMANTIC_TYPE_MAP } from '../constants';
|
||||||
import styles from './style.less';
|
import styles from './style.less';
|
||||||
import { PLACE_HOLDER } from '../constants';
|
import { PLACE_HOLDER } from '../constants';
|
||||||
import { DefaultEntityType, DomainType } from '../type';
|
import { DefaultEntityType, ModelType } from '../type';
|
||||||
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
|
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
inputMsg: string;
|
inputMsg: string;
|
||||||
chatId?: number;
|
chatId?: number;
|
||||||
currentDomain?: DomainType;
|
currentModel?: ModelType;
|
||||||
defaultEntity?: DefaultEntityType;
|
defaultEntity?: DefaultEntityType;
|
||||||
isCopilotMode?: boolean;
|
isCopilotMode?: boolean;
|
||||||
copilotFullscreen?: boolean;
|
copilotFullscreen?: boolean;
|
||||||
domains: DomainType[];
|
models: ModelType[];
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
onToggleCollapseBtn: () => void;
|
onToggleCollapseBtn: () => void;
|
||||||
onInputMsgChange: (value: string) => void;
|
onInputMsgChange: (value: string) => void;
|
||||||
onSendMsg: (msg: string, domainId?: number) => void;
|
onSendMsg: (msg: string, modelId?: number) => void;
|
||||||
onAddConversation: () => void;
|
onAddConversation: () => void;
|
||||||
onCancelDefaultFilter: () => void;
|
onCancelDefaultFilter: () => void;
|
||||||
};
|
};
|
||||||
@@ -44,9 +44,9 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
{
|
{
|
||||||
inputMsg,
|
inputMsg,
|
||||||
chatId,
|
chatId,
|
||||||
currentDomain,
|
currentModel,
|
||||||
defaultEntity,
|
defaultEntity,
|
||||||
domains,
|
models,
|
||||||
collapsed,
|
collapsed,
|
||||||
isCopilotMode,
|
isCopilotMode,
|
||||||
copilotFullscreen,
|
copilotFullscreen,
|
||||||
@@ -58,7 +58,7 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const [domainOptions, setDomainOptions] = useState<DomainType[]>([]);
|
const [modelOptions, setModelOptions] = useState<ModelType[]>([]);
|
||||||
const [stepOptions, setStepOptions] = useState<Record<string, any[]>>({});
|
const [stepOptions, setStepOptions] = useState<Record<string, any[]>>({});
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
@@ -100,7 +100,7 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getStepOptions = (recommends: any[]) => {
|
const getStepOptions = (recommends: any[]) => {
|
||||||
const data = groupByColumn(recommends, 'domainName');
|
const data = groupByColumn(recommends, 'modelName');
|
||||||
return isMobile && recommends.length > 6
|
return isMobile && recommends.length > 6
|
||||||
? Object.keys(data)
|
? Object.keys(data)
|
||||||
.slice(0, 4)
|
.slice(0, 4)
|
||||||
@@ -114,23 +114,23 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
: data;
|
: data;
|
||||||
};
|
};
|
||||||
|
|
||||||
const processMsg = (msg: string, domains: DomainType[]) => {
|
const processMsg = (msg: string, models: ModelType[]) => {
|
||||||
let msgValue = msg;
|
let msgValue = msg;
|
||||||
let domainId: number | undefined;
|
let modelId: number | undefined;
|
||||||
if (msg?.[0] === '@') {
|
if (msg?.[0] === '@') {
|
||||||
const domain = domains.find((item) => msg.includes(`@${item.name}`));
|
const model = models.find((item) => msg.includes(`@${item.name}`));
|
||||||
msgValue = domain ? msg.replace(`@${domain.name}`, '') : msg;
|
msgValue = model ? msg.replace(`@${model.name}`, '') : msg;
|
||||||
domainId = domain?.id;
|
modelId = model?.id;
|
||||||
}
|
}
|
||||||
return { msgValue, domainId };
|
return { msgValue, modelId };
|
||||||
};
|
};
|
||||||
|
|
||||||
const debounceGetWordsFunc = useCallback(() => {
|
const debounceGetWordsFunc = useCallback(() => {
|
||||||
const getAssociateWords = async (
|
const getAssociateWords = async (
|
||||||
msg: string,
|
msg: string,
|
||||||
domains: DomainType[],
|
models: ModelType[],
|
||||||
chatId?: number,
|
chatId?: number,
|
||||||
domain?: DomainType,
|
model?: ModelType,
|
||||||
) => {
|
) => {
|
||||||
if (isPinyin) {
|
if (isPinyin) {
|
||||||
return;
|
return;
|
||||||
@@ -140,9 +140,9 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
}
|
}
|
||||||
fetchRef.current += 1;
|
fetchRef.current += 1;
|
||||||
const fetchId = fetchRef.current;
|
const fetchId = fetchRef.current;
|
||||||
const { msgValue, domainId } = processMsg(msg, domains);
|
const { msgValue, modelId } = processMsg(msg, models);
|
||||||
const domainIdValue = domainId || domain?.id;
|
const modelIdValue = modelId || model?.id;
|
||||||
const res = await searchRecommend(msgValue.trim(), chatId, domainIdValue);
|
const res = await searchRecommend(msgValue.trim(), chatId, modelIdValue);
|
||||||
if (fetchId !== fetchRef.current) {
|
if (fetchId !== fetchRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -165,19 +165,19 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (inputMsg.length === 1 && inputMsg[0] === '@') {
|
if (inputMsg.length === 1 && inputMsg[0] === '@') {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
setDomainOptions(domains);
|
setModelOptions(models);
|
||||||
setStepOptions({});
|
setStepOptions({});
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
if (domainOptions.length > 0) {
|
if (modelOptions.length > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setDomainOptions([]);
|
setModelOptions([]);
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isSelect) {
|
if (!isSelect) {
|
||||||
debounceGetWords(inputMsg, domains, chatId, currentDomain);
|
debounceGetWords(inputMsg, models, chatId, currentModel);
|
||||||
} else {
|
} else {
|
||||||
isSelect = false;
|
isSelect = false;
|
||||||
}
|
}
|
||||||
@@ -219,10 +219,10 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
.find((item) =>
|
.find((item) =>
|
||||||
Object.keys(stepOptions).length === 1
|
Object.keys(stepOptions).length === 1
|
||||||
? item.recommend === value
|
? item.recommend === value
|
||||||
: `${item.domainName || ''}${item.recommend}` === value,
|
: `${item.modelName || ''}${item.recommend}` === value,
|
||||||
);
|
);
|
||||||
if (option && isSelect) {
|
if (option && isSelect) {
|
||||||
onSendMsg(option.recommend, option.domainId);
|
onSendMsg(option.recommend, option.modelId);
|
||||||
} else {
|
} else {
|
||||||
onSendMsg(value.trim());
|
onSendMsg(value.trim());
|
||||||
}
|
}
|
||||||
@@ -230,12 +230,12 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
|
|
||||||
const autoCompleteDropdownClass = classNames(styles.autoCompleteDropdown, {
|
const autoCompleteDropdownClass = classNames(styles.autoCompleteDropdown, {
|
||||||
[styles.mobile]: isMobile,
|
[styles.mobile]: isMobile,
|
||||||
[styles.domainOptions]: domainOptions.length > 0,
|
[styles.modelOptions]: modelOptions.length > 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSelect = (value: string) => {
|
const onSelect = (value: string) => {
|
||||||
isSelect = true;
|
isSelect = true;
|
||||||
if (domainOptions.length === 0) {
|
if (modelOptions.length === 0) {
|
||||||
sendMsg(value);
|
sendMsg(value);
|
||||||
}
|
}
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
@@ -263,19 +263,16 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div className={styles.composerInputWrapper}>
|
<div className={styles.composerInputWrapper}>
|
||||||
{currentDomain && (
|
{currentModel && (
|
||||||
<div className={styles.currentDomain}>
|
<div className={styles.currentModel}>
|
||||||
<div className={styles.currentDomainName}>
|
<div className={styles.currentModelName}>
|
||||||
输入联想与问题回复将限定于:“
|
输入联想与问题回复将限定于:“
|
||||||
<span className={styles.quoteText}>
|
<span className={styles.quoteText}>
|
||||||
主题域【{currentDomain.name}】
|
主题域【{currentModel.name}】
|
||||||
{defaultEntity && (
|
{defaultEntity && (
|
||||||
<>
|
<>
|
||||||
<span>,</span>
|
<span>,</span>
|
||||||
<span>{`${currentDomain.name.slice(
|
<span>{`${currentModel.name.slice(0, currentModel.name.length - 1)}【`}</span>
|
||||||
0,
|
|
||||||
currentDomain.name.length - 1,
|
|
||||||
)}【`}</span>
|
|
||||||
<span className={styles.entityName} title={defaultEntity.entityName}>
|
<span className={styles.entityName} title={defaultEntity.entityName}>
|
||||||
{defaultEntity.entityName}
|
{defaultEntity.entityName}
|
||||||
</span>
|
</span>
|
||||||
@@ -285,7 +282,7 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
</span>
|
</span>
|
||||||
”
|
”
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.cancelDomain} onClick={onCancelDefaultFilter}>
|
<div className={styles.cancelModel} onClick={onCancelDefaultFilter}>
|
||||||
取消限定
|
取消限定
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -293,8 +290,8 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
<AutoComplete
|
<AutoComplete
|
||||||
className={styles.composerInput}
|
className={styles.composerInput}
|
||||||
placeholder={
|
placeholder={
|
||||||
currentDomain
|
currentModel
|
||||||
? `请输入【${currentDomain.name}】主题的问题,可使用@切换到其他主题`
|
? `请输入【${currentModel.name}】主题的问题,可使用@切换到其他主题`
|
||||||
: PLACE_HOLDER
|
: PLACE_HOLDER
|
||||||
}
|
}
|
||||||
value={inputMsg}
|
value={inputMsg}
|
||||||
@@ -323,15 +320,15 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
open={open}
|
open={open}
|
||||||
getPopupContainer={(triggerNode) => triggerNode.parentNode}
|
getPopupContainer={(triggerNode) => triggerNode.parentNode}
|
||||||
>
|
>
|
||||||
{domainOptions.length > 0
|
{modelOptions.length > 0
|
||||||
? domainOptions.map((domain) => {
|
? modelOptions.map((model) => {
|
||||||
return (
|
return (
|
||||||
<Option
|
<Option
|
||||||
key={domain.id}
|
key={model.id}
|
||||||
value={`@${domain.name} `}
|
value={`@${model.name} `}
|
||||||
className={styles.searchOption}
|
className={styles.searchOption}
|
||||||
>
|
>
|
||||||
{domain.name}
|
{model.name}
|
||||||
</Option>
|
</Option>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@@ -342,17 +339,15 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
let optionValue =
|
let optionValue =
|
||||||
Object.keys(stepOptions).length === 1
|
Object.keys(stepOptions).length === 1
|
||||||
? option.recommend
|
? option.recommend
|
||||||
: `${option.domainName || ''}${option.recommend}`;
|
: `${option.modelName || ''}${option.recommend}`;
|
||||||
if (inputMsg[0] === '@') {
|
if (inputMsg[0] === '@') {
|
||||||
const domain = domains.find((item) => inputMsg.includes(item.name));
|
const model = models.find((item) => inputMsg.includes(item.name));
|
||||||
optionValue = domain
|
optionValue = model ? `@${model.name} ${option.recommend}` : optionValue;
|
||||||
? `@${domain.name} ${option.recommend}`
|
|
||||||
: optionValue;
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Option
|
<Option
|
||||||
key={`${option.recommend}${
|
key={`${option.recommend}${
|
||||||
option.domainName ? `_${option.domainName}` : ''
|
option.modelName ? `_${option.modelName}` : ''
|
||||||
}`}
|
}`}
|
||||||
value={optionValue}
|
value={optionValue}
|
||||||
className={styles.searchOption}
|
className={styles.searchOption}
|
||||||
@@ -363,7 +358,7 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
|||||||
className={styles.semanticType}
|
className={styles.semanticType}
|
||||||
color={
|
color={
|
||||||
option.schemaElementType === SemanticTypeEnum.DIMENSION ||
|
option.schemaElementType === SemanticTypeEnum.DIMENSION ||
|
||||||
option.schemaElementType === SemanticTypeEnum.DOMAIN
|
option.schemaElementType === SemanticTypeEnum.MODEL
|
||||||
? 'blue'
|
? 'blue'
|
||||||
: option.schemaElementType === SemanticTypeEnum.VALUE
|
: option.schemaElementType === SemanticTypeEnum.VALUE
|
||||||
? 'geekblue'
|
? 'geekblue'
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
.currentDomain {
|
.currentModel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -30px;
|
top: -30px;
|
||||||
left: 15px;
|
left: 15px;
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
border-top-left-radius: 6px;
|
border-top-left-radius: 6px;
|
||||||
border-top-right-radius: 6px;
|
border-top-right-radius: 6px;
|
||||||
|
|
||||||
.currentDomainName {
|
.currentModelName {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cancelDomain {
|
.cancelModel {
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
border: 1px solid var(--text-color-fourth);
|
border: 1px solid var(--text-color-fourth);
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.domain {
|
.model {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
color: var(--text-color-fourth);
|
color: var(--text-color-fourth);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
min-width: 100px !important;
|
min-width: 100px !important;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|
||||||
&.domainOptions {
|
&.modelOptions {
|
||||||
width: 150px !important;
|
width: 150px !important;
|
||||||
|
|
||||||
.searchOption {
|
.searchOption {
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ type Props = {
|
|||||||
currentConversation?: ConversationDetailType;
|
currentConversation?: ConversationDetailType;
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
isCopilotMode?: boolean;
|
isCopilotMode?: boolean;
|
||||||
defaultDomainName?: string;
|
defaultModelName?: string;
|
||||||
defaultEntityFilter?: DefaultEntityType;
|
defaultEntityFilter?: DefaultEntityType;
|
||||||
triggerNewConversation?: boolean;
|
triggerNewConversation?: boolean;
|
||||||
onNewConversationTriggered?: () => void;
|
onNewConversationTriggered?: () => void;
|
||||||
onSelectConversation: (
|
onSelectConversation: (
|
||||||
conversation: ConversationDetailType,
|
conversation: ConversationDetailType,
|
||||||
name?: string,
|
name?: string,
|
||||||
domainId?: number,
|
modelId?: number,
|
||||||
entityId?: string,
|
entityId?: string,
|
||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
@@ -38,7 +38,7 @@ const Conversation: ForwardRefRenderFunction<any, Props> = (
|
|||||||
currentConversation,
|
currentConversation,
|
||||||
collapsed,
|
collapsed,
|
||||||
isCopilotMode,
|
isCopilotMode,
|
||||||
defaultDomainName,
|
defaultModelName,
|
||||||
defaultEntityFilter,
|
defaultEntityFilter,
|
||||||
triggerNewConversation,
|
triggerNewConversation,
|
||||||
onNewConversationTriggered,
|
onNewConversationTriggered,
|
||||||
@@ -47,7 +47,7 @@ const Conversation: ForwardRefRenderFunction<any, Props> = (
|
|||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { q, cid, domainId, entityId } = (location as any).query;
|
const { q, cid, modelId, entityId } = (location as any).query;
|
||||||
const [conversations, setConversations] = useState<ConversationDetailType[]>([]);
|
const [conversations, setConversations] = useState<ConversationDetailType[]>([]);
|
||||||
const [editModalVisible, setEditModalVisible] = useState(false);
|
const [editModalVisible, setEditModalVisible] = useState(false);
|
||||||
const [editConversation, setEditConversation] = useState<ConversationDetailType>();
|
const [editConversation, setEditConversation] = useState<ConversationDetailType>();
|
||||||
@@ -89,7 +89,7 @@ const Conversation: ForwardRefRenderFunction<any, Props> = (
|
|||||||
const conversationName =
|
const conversationName =
|
||||||
defaultEntityFilter?.entityName && window.location.pathname.includes('detail')
|
defaultEntityFilter?.entityName && window.location.pathname.includes('detail')
|
||||||
? defaultEntityFilter.entityName
|
? defaultEntityFilter.entityName
|
||||||
: defaultDomainName;
|
: defaultModelName;
|
||||||
onAddConversation({ name: conversationName, type: 'CUSTOMIZE' });
|
onAddConversation({ name: conversationName, type: 'CUSTOMIZE' });
|
||||||
onNewConversationTriggered?.();
|
onNewConversationTriggered?.();
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ const Conversation: ForwardRefRenderFunction<any, Props> = (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (q && cid === undefined && window.location.href.includes('/workbench/chat')) {
|
if (q && cid === undefined && window.location.href.includes('/workbench/chat')) {
|
||||||
onAddConversation({ name: q, domainId: domainId ? +domainId : undefined, entityId });
|
onAddConversation({ name: q, modelId: modelId ? +modelId : undefined, entityId });
|
||||||
} else {
|
} else {
|
||||||
initData();
|
initData();
|
||||||
}
|
}
|
||||||
@@ -118,17 +118,17 @@ const Conversation: ForwardRefRenderFunction<any, Props> = (
|
|||||||
|
|
||||||
const onAddConversation = async ({
|
const onAddConversation = async ({
|
||||||
name,
|
name,
|
||||||
domainId,
|
modelId,
|
||||||
entityId,
|
entityId,
|
||||||
type,
|
type,
|
||||||
}: {
|
}: {
|
||||||
name?: string;
|
name?: string;
|
||||||
domainId?: number;
|
modelId?: number;
|
||||||
entityId?: string;
|
entityId?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
const data = await addConversation(name);
|
const data = await addConversation(name);
|
||||||
onSelectConversation(data[0], type || name, domainId, entityId);
|
onSelectConversation(data[0], type || name, modelId, entityId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOperate = (key: string, conversation: ConversationDetailType) => {
|
const onOperate = (key: string, conversation: ConversationDetailType) => {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type Props = {
|
|||||||
valid: boolean,
|
valid: boolean,
|
||||||
) => void;
|
) => void;
|
||||||
onCheckMore: (data: MsgDataType) => void;
|
onCheckMore: (data: MsgDataType) => void;
|
||||||
onApplyAuth: (domain: string) => void;
|
onApplyAuth: (model: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MessageContainer: React.FC<Props> = ({
|
const MessageContainer: React.FC<Props> = ({
|
||||||
@@ -71,15 +71,15 @@ const MessageContainer: React.FC<Props> = ({
|
|||||||
|
|
||||||
for (let i = 0; i < msgs.length; i++) {
|
for (let i = 0; i < msgs.length; i++) {
|
||||||
const msg = msgs[i];
|
const msg = msgs[i];
|
||||||
const msgDomainId = msg.msgData?.chatContext?.domainId;
|
const msgModelId = msg.msgData?.chatContext?.modelId;
|
||||||
const msgEntityId = msg.msgData?.entityInfo?.entityId;
|
const msgEntityId = msg.msgData?.entityInfo?.entityId;
|
||||||
const currentMsgDomainId = currentMsgData?.chatContext?.domainId;
|
const currentMsgModelId = currentMsgData?.chatContext?.modelId;
|
||||||
const currentMsgEntityId = currentMsgData?.entityInfo?.entityId;
|
const currentMsgEntityId = currentMsgData?.entityInfo?.entityId;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(msg.type === MessageTypeEnum.QUESTION || msg.type === MessageTypeEnum.PLUGIN) &&
|
(msg.type === MessageTypeEnum.QUESTION || msg.type === MessageTypeEnum.PLUGIN) &&
|
||||||
!!currentMsgDomainId &&
|
!!currentMsgModelId &&
|
||||||
msgDomainId === currentMsgDomainId &&
|
msgModelId === currentMsgModelId &&
|
||||||
msgEntityId === currentMsgEntityId &&
|
msgEntityId === currentMsgEntityId &&
|
||||||
msg.msg
|
msg.msg
|
||||||
) {
|
) {
|
||||||
@@ -91,8 +91,8 @@ const MessageContainer: React.FC<Props> = ({
|
|||||||
return followQuestions;
|
return followQuestions;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFilters = (domainId?: number, entityId?: string) => {
|
const getFilters = (modelId?: number, entityId?: string) => {
|
||||||
if (!domainId || !entityId) {
|
if (!modelId || !entityId) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
@@ -108,7 +108,7 @@ const MessageContainer: React.FC<Props> = ({
|
|||||||
{messageList.map((msgItem: MessageItem, index: number) => {
|
{messageList.map((msgItem: MessageItem, index: number) => {
|
||||||
const {
|
const {
|
||||||
id: msgId,
|
id: msgId,
|
||||||
domainId,
|
modelId,
|
||||||
entityId,
|
entityId,
|
||||||
type,
|
type,
|
||||||
msg,
|
msg,
|
||||||
@@ -117,6 +117,7 @@ const MessageContainer: React.FC<Props> = ({
|
|||||||
msgData,
|
msgData,
|
||||||
score,
|
score,
|
||||||
isHistory,
|
isHistory,
|
||||||
|
parseOptions,
|
||||||
} = msgItem;
|
} = msgItem;
|
||||||
|
|
||||||
const followQuestions = getFollowQuestions(index);
|
const followQuestions = getFollowQuestions(index);
|
||||||
@@ -132,8 +133,8 @@ const MessageContainer: React.FC<Props> = ({
|
|||||||
msg={msgValue || msg || ''}
|
msg={msgValue || msg || ''}
|
||||||
msgData={msgData}
|
msgData={msgData}
|
||||||
conversationId={chatId}
|
conversationId={chatId}
|
||||||
domainId={domainId}
|
modelId={modelId}
|
||||||
filter={getFilters(domainId, entityId)}
|
filter={getFilters(modelId, entityId)}
|
||||||
isLastMessage={index === messageList.length - 1}
|
isLastMessage={index === messageList.length - 1}
|
||||||
isMobileMode={isMobileMode}
|
isMobileMode={isMobileMode}
|
||||||
triggerResize={triggerResize}
|
triggerResize={triggerResize}
|
||||||
@@ -144,6 +145,22 @@ const MessageContainer: React.FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{type === MessageTypeEnum.PARSE_OPTIONS && (
|
||||||
|
<ChatItem
|
||||||
|
msg={msgValue || msg || ''}
|
||||||
|
conversationId={chatId}
|
||||||
|
modelId={modelId}
|
||||||
|
filter={getFilters(modelId, entityId)}
|
||||||
|
isLastMessage={index === messageList.length - 1}
|
||||||
|
isMobileMode={isMobileMode}
|
||||||
|
triggerResize={triggerResize}
|
||||||
|
parseOptions={parseOptions}
|
||||||
|
onMsgDataLoaded={(data: MsgDataType, valid: boolean) => {
|
||||||
|
onMsgDataLoaded(data, msgId, msgValue || msg || '', valid);
|
||||||
|
}}
|
||||||
|
onUpdateMessageScroll={updateMessageContainerScroll}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{type === MessageTypeEnum.PLUGIN && (
|
{type === MessageTypeEnum.PLUGIN && (
|
||||||
<>
|
<>
|
||||||
<Plugin
|
<Plugin
|
||||||
|
|||||||
@@ -6,42 +6,16 @@ type Props = {
|
|||||||
width?: number | string;
|
width?: number | string;
|
||||||
height?: number | string;
|
height?: number | string;
|
||||||
bubbleClassName?: string;
|
bubbleClassName?: string;
|
||||||
domainName?: string;
|
|
||||||
question?: string;
|
|
||||||
followQuestions?: string[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Message: React.FC<Props> = ({
|
const Message: React.FC<Props> = ({ position, width, height, children, bubbleClassName }) => {
|
||||||
position,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
children,
|
|
||||||
bubbleClassName,
|
|
||||||
domainName,
|
|
||||||
question,
|
|
||||||
followQuestions,
|
|
||||||
}) => {
|
|
||||||
const messageClass = classNames(styles.message, {
|
const messageClass = classNames(styles.message, {
|
||||||
[styles.left]: position === 'left',
|
[styles.left]: position === 'left',
|
||||||
[styles.right]: position === 'right',
|
[styles.right]: position === 'right',
|
||||||
});
|
});
|
||||||
|
|
||||||
const leftTitle = question
|
|
||||||
? followQuestions && followQuestions.length > 0
|
|
||||||
? `多轮对话:${[question, ...followQuestions].join(' ← ')}`
|
|
||||||
: `单轮对话:${question}`
|
|
||||||
: '';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={messageClass} style={{ width }}>
|
<div className={messageClass} style={{ width }}>
|
||||||
{/* <div className={styles.messageTitleBar}>
|
|
||||||
{!!domainName && <div className={styles.domainName}>{domainName}</div>}
|
|
||||||
{position === 'left' && leftTitle && (
|
|
||||||
<div className={styles.messageTopBar} title={leftTitle}>
|
|
||||||
({leftTitle})
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div> */}
|
|
||||||
<div className={styles.messageContent}>
|
<div className={styles.messageContent}>
|
||||||
<div className={styles.messageBody}>
|
<div className={styles.messageBody}>
|
||||||
<div
|
<div
|
||||||
@@ -51,11 +25,6 @@ const Message: React.FC<Props> = ({
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* {position === 'left' && question && (
|
|
||||||
<div className={styles.messageTopBar} title={leftTitle}>
|
|
||||||
{leftTitle}
|
|
||||||
</div>
|
|
||||||
)} */}
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -161,15 +161,7 @@ const Plugin: React.FC<Props> = ({
|
|||||||
<div className={reportClass}>
|
<div className={reportClass}>
|
||||||
<LeftAvatar />
|
<LeftAvatar />
|
||||||
<div className={styles.msgContent}>
|
<div className={styles.msgContent}>
|
||||||
<Message
|
<Message position="left" width="100%" height={height} bubbleClassName={styles.reportBubble}>
|
||||||
position="left"
|
|
||||||
width="100%"
|
|
||||||
height={height}
|
|
||||||
bubbleClassName={styles.reportBubble}
|
|
||||||
domainName={data.chatContext?.domainName}
|
|
||||||
question={msg}
|
|
||||||
followQuestions={followQuestions}
|
|
||||||
>
|
|
||||||
<iframe
|
<iframe
|
||||||
id={`reportIframe_${id}`}
|
id={`reportIframe_${id}`}
|
||||||
src={pluginUrl}
|
src={pluginUrl}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
column-gap: 10px;
|
column-gap: 10px;
|
||||||
|
|
||||||
.domainName {
|
.modelName {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ export const THEME_COLOR_LIST = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export enum SemanticTypeEnum {
|
export enum SemanticTypeEnum {
|
||||||
DOMAIN = 'DOMAIN',
|
MODEL = 'MODEL',
|
||||||
DIMENSION = 'DIMENSION',
|
DIMENSION = 'DIMENSION',
|
||||||
METRIC = 'METRIC',
|
METRIC = 'METRIC',
|
||||||
VALUE = 'VALUE',
|
VALUE = 'VALUE',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SEMANTIC_TYPE_MAP = {
|
export const SEMANTIC_TYPE_MAP = {
|
||||||
[SemanticTypeEnum.DOMAIN]: '主题域',
|
[SemanticTypeEnum.MODEL]: '主题域',
|
||||||
[SemanticTypeEnum.DIMENSION]: '维度',
|
[SemanticTypeEnum.DIMENSION]: '维度',
|
||||||
[SemanticTypeEnum.METRIC]: '指标',
|
[SemanticTypeEnum.METRIC]: '指标',
|
||||||
[SemanticTypeEnum.VALUE]: '维度值',
|
[SemanticTypeEnum.VALUE]: '维度值',
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import styles from './style.less';
|
|||||||
import {
|
import {
|
||||||
ConversationDetailType,
|
ConversationDetailType,
|
||||||
DefaultEntityType,
|
DefaultEntityType,
|
||||||
DomainType,
|
ModelType,
|
||||||
MessageItem,
|
MessageItem,
|
||||||
MessageTypeEnum,
|
MessageTypeEnum,
|
||||||
} from './type';
|
} from './type';
|
||||||
import { getDomainList } from './service';
|
import { getModelList } from './service';
|
||||||
import { useThrottleFn } from 'ahooks';
|
import { useThrottleFn } from 'ahooks';
|
||||||
import Conversation from './Conversation';
|
import Conversation from './Conversation';
|
||||||
import ChatFooter from './ChatFooter';
|
import ChatFooter from './ChatFooter';
|
||||||
@@ -25,12 +25,12 @@ import { AUTH_TOKEN_KEY } from '@/common/constants';
|
|||||||
type Props = {
|
type Props = {
|
||||||
isCopilotMode?: boolean;
|
isCopilotMode?: boolean;
|
||||||
copilotFullscreen?: boolean;
|
copilotFullscreen?: boolean;
|
||||||
defaultDomainName?: string;
|
defaultModelName?: string;
|
||||||
defaultEntityFilter?: DefaultEntityType;
|
defaultEntityFilter?: DefaultEntityType;
|
||||||
copilotSendMsg?: string;
|
copilotSendMsg?: string;
|
||||||
triggerNewConversation?: boolean;
|
triggerNewConversation?: boolean;
|
||||||
onNewConversationTriggered?: () => void;
|
onNewConversationTriggered?: () => void;
|
||||||
onCurrentDomainChange?: (domain?: DomainType) => void;
|
onCurrentModelChange?: (model?: ModelType) => void;
|
||||||
onCancelCopilotFilter?: () => void;
|
onCancelCopilotFilter?: () => void;
|
||||||
onCheckMoreDetail?: () => void;
|
onCheckMoreDetail?: () => void;
|
||||||
};
|
};
|
||||||
@@ -38,17 +38,16 @@ type Props = {
|
|||||||
const Chat: React.FC<Props> = ({
|
const Chat: React.FC<Props> = ({
|
||||||
isCopilotMode,
|
isCopilotMode,
|
||||||
copilotFullscreen,
|
copilotFullscreen,
|
||||||
defaultDomainName,
|
defaultModelName,
|
||||||
defaultEntityFilter,
|
defaultEntityFilter,
|
||||||
copilotSendMsg,
|
copilotSendMsg,
|
||||||
triggerNewConversation,
|
triggerNewConversation,
|
||||||
onNewConversationTriggered,
|
onNewConversationTriggered,
|
||||||
onCurrentDomainChange,
|
onCurrentModelChange,
|
||||||
onCancelCopilotFilter,
|
onCancelCopilotFilter,
|
||||||
onCheckMoreDetail,
|
onCheckMoreDetail,
|
||||||
}) => {
|
}) => {
|
||||||
const isMobileMode = isMobile || isCopilotMode;
|
const isMobileMode = isMobile || isCopilotMode;
|
||||||
const localConversationCollapsed = localStorage.getItem('CONVERSATION_COLLAPSED');
|
|
||||||
|
|
||||||
const [messageList, setMessageList] = useState<MessageItem[]>([]);
|
const [messageList, setMessageList] = useState<MessageItem[]>([]);
|
||||||
const [inputMsg, setInputMsg] = useState('');
|
const [inputMsg, setInputMsg] = useState('');
|
||||||
@@ -58,54 +57,52 @@ const Chat: React.FC<Props> = ({
|
|||||||
const [currentConversation, setCurrentConversation] = useState<
|
const [currentConversation, setCurrentConversation] = useState<
|
||||||
ConversationDetailType | undefined
|
ConversationDetailType | undefined
|
||||||
>(isMobile ? { chatId: 0, chatName: `${CHAT_TITLE}问答` } : undefined);
|
>(isMobile ? { chatId: 0, chatName: `${CHAT_TITLE}问答` } : undefined);
|
||||||
const [conversationCollapsed, setConversationCollapsed] = useState(
|
const [conversationCollapsed, setConversationCollapsed] = useState(isCopilotMode);
|
||||||
!localConversationCollapsed ? true : localConversationCollapsed === 'true',
|
const [models, setModels] = useState<ModelType[]>([]);
|
||||||
);
|
const [currentModel, setCurrentModel] = useState<ModelType>();
|
||||||
const [domains, setDomains] = useState<DomainType[]>([]);
|
|
||||||
const [currentDomain, setCurrentDomain] = useState<DomainType>();
|
|
||||||
const [defaultEntity, setDefaultEntity] = useState<DefaultEntityType>();
|
const [defaultEntity, setDefaultEntity] = useState<DefaultEntityType>();
|
||||||
const [applyAuthVisible, setApplyAuthVisible] = useState(false);
|
const [applyAuthVisible, setApplyAuthVisible] = useState(false);
|
||||||
const [applyAuthDomain, setApplyAuthDomain] = useState('');
|
const [applyAuthModel, setApplyAuthModel] = useState('');
|
||||||
const [initialDomainName, setInitialDomainName] = useState('');
|
const [initialModelName, setInitialModelName] = useState('');
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { domainName } = (location as any).query;
|
const { modelName } = (location as any).query;
|
||||||
|
|
||||||
const conversationRef = useRef<any>();
|
const conversationRef = useRef<any>();
|
||||||
const chatFooterRef = useRef<any>();
|
const chatFooterRef = useRef<any>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setChatSdkToken(localStorage.getItem(AUTH_TOKEN_KEY) || '');
|
setChatSdkToken(localStorage.getItem(AUTH_TOKEN_KEY) || '');
|
||||||
initDomains();
|
initModels();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (domains.length > 0 && initialDomainName && !currentDomain) {
|
if (models.length > 0 && initialModelName && !currentModel) {
|
||||||
changeDomain(domains.find((domain) => domain.name === initialDomainName));
|
changeModel(models.find((model) => model.name === initialModelName));
|
||||||
}
|
}
|
||||||
}, [domains]);
|
}, [models]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (domainName) {
|
if (modelName) {
|
||||||
setInitialDomainName(domainName);
|
setInitialModelName(modelName);
|
||||||
}
|
}
|
||||||
}, [domainName]);
|
}, [modelName]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (defaultDomainName !== undefined && domains.length > 0) {
|
if (defaultModelName !== undefined && models.length > 0) {
|
||||||
changeDomain(domains.find((domain) => domain.name === defaultDomainName));
|
changeModel(models.find((model) => model.name === defaultModelName));
|
||||||
}
|
}
|
||||||
}, [defaultDomainName]);
|
}, [defaultModelName]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentConversation) {
|
if (!currentConversation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { initMsg, domainId, entityId } = currentConversation;
|
const { initMsg, modelId, entityId } = currentConversation;
|
||||||
if (initMsg) {
|
if (initMsg) {
|
||||||
inputFocus();
|
inputFocus();
|
||||||
if (initMsg === 'CUSTOMIZE' && copilotSendMsg) {
|
if (initMsg === 'CUSTOMIZE' && copilotSendMsg) {
|
||||||
onSendMsg(copilotSendMsg, [], domainId, entityId);
|
onSendMsg(copilotSendMsg, [], modelId, entityId);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'globalState/setCopilotSendMsg',
|
type: 'globalState/setCopilotSendMsg',
|
||||||
payload: '',
|
payload: '',
|
||||||
@@ -116,7 +113,7 @@ const Chat: React.FC<Props> = ({
|
|||||||
sendHelloRsp();
|
sendHelloRsp();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onSendMsg(initMsg, [], domainId, entityId);
|
onSendMsg(initMsg, [], modelId, entityId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateHistoryMsg(1);
|
updateHistoryMsg(1);
|
||||||
@@ -147,13 +144,13 @@ const Chat: React.FC<Props> = ({
|
|||||||
{
|
{
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
type: MessageTypeEnum.TEXT,
|
type: MessageTypeEnum.TEXT,
|
||||||
msg: defaultDomainName
|
msg: defaultModelName
|
||||||
? `您好,请输入关于${
|
? `您好,请输入关于${
|
||||||
defaultEntityFilter?.entityName
|
defaultEntityFilter?.entityName
|
||||||
? `${defaultDomainName?.slice(0, defaultDomainName?.length - 1)}【${
|
? `${defaultModelName?.slice(0, defaultModelName?.length - 1)}【${
|
||||||
defaultEntityFilter?.entityName
|
defaultEntityFilter?.entityName
|
||||||
}】`
|
}】`
|
||||||
: `【${defaultDomainName}】`
|
: `【${defaultModelName}】`
|
||||||
}的问题`
|
}的问题`
|
||||||
: '您好,请问有什么我能帮您吗?',
|
: '您好,请问有什么我能帮您吗?',
|
||||||
},
|
},
|
||||||
@@ -208,19 +205,19 @@ const Chat: React.FC<Props> = ({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const changeDomain = (domain?: DomainType) => {
|
const changeModel = (model?: ModelType) => {
|
||||||
setCurrentDomain(domain);
|
setCurrentModel(model);
|
||||||
if (onCurrentDomainChange) {
|
if (onCurrentModelChange) {
|
||||||
onCurrentDomainChange(domain);
|
onCurrentModelChange(model);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const initDomains = async () => {
|
const initModels = async () => {
|
||||||
const res = await getDomainList();
|
const res = await getModelList();
|
||||||
const domainList = getLeafList(res.data);
|
const modelList = getLeafList(res.data);
|
||||||
setDomains([{ id: -1, name: '全部', bizName: 'all', parentId: 0 }, ...domainList].slice(0, 11));
|
setModels([{ id: -1, name: '全部', bizName: 'all', parentId: 0 }, ...modelList].slice(0, 11));
|
||||||
if (defaultDomainName !== undefined) {
|
if (defaultModelName !== undefined) {
|
||||||
changeDomain(domainList.find((domain) => domain.name === defaultDomainName));
|
changeModel(modelList.find((model) => model.name === defaultModelName));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -237,7 +234,7 @@ const Chat: React.FC<Props> = ({
|
|||||||
const onSendMsg = async (
|
const onSendMsg = async (
|
||||||
msg?: string,
|
msg?: string,
|
||||||
list?: MessageItem[],
|
list?: MessageItem[],
|
||||||
domainId?: number,
|
modelId?: number,
|
||||||
entityId?: string,
|
entityId?: string,
|
||||||
) => {
|
) => {
|
||||||
const currentMsg = msg || inputMsg;
|
const currentMsg = msg || inputMsg;
|
||||||
@@ -245,25 +242,25 @@ const Chat: React.FC<Props> = ({
|
|||||||
setInputMsg('');
|
setInputMsg('');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const msgDomain = domains.find((item) => currentMsg.includes(item.name));
|
const msgModel = models.find((item) => currentMsg.includes(item.name));
|
||||||
const certainDomain = currentMsg[0] === '@' && msgDomain;
|
const certainModel = currentMsg[0] === '@' && msgModel;
|
||||||
let domainChanged = false;
|
let modelChanged = false;
|
||||||
|
|
||||||
if (certainDomain) {
|
if (certainModel) {
|
||||||
const toDomain = msgDomain.id === -1 ? undefined : msgDomain;
|
const toModel = msgModel.id === -1 ? undefined : msgModel;
|
||||||
changeDomain(toDomain);
|
changeModel(toModel);
|
||||||
domainChanged = currentDomain?.id !== toDomain?.id;
|
modelChanged = currentModel?.id !== toModel?.id;
|
||||||
}
|
}
|
||||||
const domainIdValue = domainId || msgDomain?.id || currentDomain?.id;
|
const modelIdValue = modelId || msgModel?.id || currentModel?.id;
|
||||||
const msgs = [
|
const msgs = [
|
||||||
...(list || messageList),
|
...(list || messageList),
|
||||||
{
|
{
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
msg: currentMsg,
|
msg: currentMsg,
|
||||||
msgValue: certainDomain ? currentMsg.replace(`@${msgDomain.name}`, '').trim() : currentMsg,
|
msgValue: certainModel ? currentMsg.replace(`@${msgModel.name}`, '').trim() : currentMsg,
|
||||||
domainId: domainIdValue === -1 ? undefined : domainIdValue,
|
modelId: modelIdValue === -1 ? undefined : modelIdValue,
|
||||||
entityId: entityId || (domainChanged ? undefined : defaultEntity?.entityId),
|
entityId: entityId || (modelChanged ? undefined : defaultEntity?.entityId),
|
||||||
identityMsg: certainDomain ? getIdentityMsgText(msgDomain) : undefined,
|
identityMsg: certainModel ? getIdentityMsgText(msgModel) : undefined,
|
||||||
type: MessageTypeEnum.QUESTION,
|
type: MessageTypeEnum.QUESTION,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -290,7 +287,7 @@ const Chat: React.FC<Props> = ({
|
|||||||
const onSelectConversation = (
|
const onSelectConversation = (
|
||||||
conversation: ConversationDetailType,
|
conversation: ConversationDetailType,
|
||||||
name?: string,
|
name?: string,
|
||||||
domainId?: number,
|
modelId?: number,
|
||||||
entityId?: string,
|
entityId?: string,
|
||||||
) => {
|
) => {
|
||||||
if (!isMobileMode) {
|
if (!isMobileMode) {
|
||||||
@@ -299,12 +296,31 @@ const Chat: React.FC<Props> = ({
|
|||||||
setCurrentConversation({
|
setCurrentConversation({
|
||||||
...conversation,
|
...conversation,
|
||||||
initMsg: name,
|
initMsg: name,
|
||||||
domainId,
|
modelId,
|
||||||
entityId,
|
entityId,
|
||||||
});
|
});
|
||||||
saveConversationToLocal(conversation);
|
saveConversationToLocal(conversation);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateChatFilter = (data: MsgDataType) => {
|
||||||
|
const { queryMode, dimensionFilters, elementMatches, modelName, model } = data.chatContext;
|
||||||
|
if (queryMode !== 'ENTITY_LIST_FILTER') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const entityId = dimensionFilters?.length > 0 ? dimensionFilters[0].value : undefined;
|
||||||
|
const entityName = elementMatches?.find((item: any) => item.element?.type === 'ID')?.element
|
||||||
|
?.name;
|
||||||
|
|
||||||
|
if (typeof entityId === 'string' && entityName) {
|
||||||
|
setCurrentModel(model);
|
||||||
|
setDefaultEntity({
|
||||||
|
entityId,
|
||||||
|
entityName,
|
||||||
|
modelName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onMsgDataLoaded = (data: MsgDataType, questionId: string | number) => {
|
const onMsgDataLoaded = (data: MsgDataType, questionId: string | number) => {
|
||||||
if (!isMobile) {
|
if (!isMobile) {
|
||||||
conversationRef?.current?.updateData();
|
conversationRef?.current?.updateData();
|
||||||
@@ -312,6 +328,15 @@ const Chat: React.FC<Props> = ({
|
|||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
if (data.queryMode === 'WEB_PAGE') {
|
if (data.queryMode === 'WEB_PAGE') {
|
||||||
setMessageList([
|
setMessageList([
|
||||||
...messageList,
|
...messageList,
|
||||||
@@ -321,16 +346,19 @@ const Chat: React.FC<Props> = ({
|
|||||||
type: MessageTypeEnum.PLUGIN,
|
type: MessageTypeEnum.PLUGIN,
|
||||||
msgData: data,
|
msgData: data,
|
||||||
},
|
},
|
||||||
|
...(parseOptionsItem ? [parseOptionsItem] : []),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
const msgs = cloneDeep(messageList);
|
const msgs = cloneDeep(messageList);
|
||||||
const msg = msgs.find((item) => item.id === questionId);
|
const msg = msgs.find((item) => item.id === questionId);
|
||||||
if (msg) {
|
if (msg) {
|
||||||
msg.msgData = data;
|
msg.msgData = data;
|
||||||
setMessageList(msgs);
|
setMessageList([...msgs, ...(parseOptionsItem ? [parseOptionsItem] : [])]);
|
||||||
}
|
}
|
||||||
updateMessageContainerScroll();
|
updateMessageContainerScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateChatFilter(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCheckMore = (data: MsgDataType) => {
|
const onCheckMore = (data: MsgDataType) => {
|
||||||
@@ -354,14 +382,14 @@ const Chat: React.FC<Props> = ({
|
|||||||
localStorage.setItem('CONVERSATION_COLLAPSED', `${!conversationCollapsed}`);
|
localStorage.setItem('CONVERSATION_COLLAPSED', `${!conversationCollapsed}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIdentityMsgText = (domain?: DomainType) => {
|
const getIdentityMsgText = (model?: ModelType) => {
|
||||||
return domain
|
return model
|
||||||
? `您好,我当前身份是【${domain.name}】主题专家,我将尽力帮您解答相关问题~`
|
? `您好,我当前身份是【${model.name}】主题专家,我将尽力帮您解答相关问题~`
|
||||||
: '您好,我将尽力帮您解答所有主题相关问题~';
|
: '您好,我将尽力帮您解答所有主题相关问题~';
|
||||||
};
|
};
|
||||||
|
|
||||||
const onApplyAuth = (domain: string) => {
|
const onApplyAuth = (model: string) => {
|
||||||
setApplyAuthDomain(domain);
|
setApplyAuthModel(model);
|
||||||
setApplyAuthVisible(true);
|
setApplyAuthVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -385,7 +413,7 @@ const Chat: React.FC<Props> = ({
|
|||||||
currentConversation={currentConversation}
|
currentConversation={currentConversation}
|
||||||
collapsed={conversationCollapsed}
|
collapsed={conversationCollapsed}
|
||||||
isCopilotMode={isCopilotMode}
|
isCopilotMode={isCopilotMode}
|
||||||
defaultDomainName={defaultDomainName}
|
defaultModelName={defaultModelName}
|
||||||
defaultEntityFilter={defaultEntityFilter}
|
defaultEntityFilter={defaultEntityFilter}
|
||||||
triggerNewConversation={triggerNewConversation}
|
triggerNewConversation={triggerNewConversation}
|
||||||
onNewConversationTriggered={onNewConversationTriggered}
|
onNewConversationTriggered={onNewConversationTriggered}
|
||||||
@@ -411,23 +439,24 @@ const Chat: React.FC<Props> = ({
|
|||||||
<ChatFooter
|
<ChatFooter
|
||||||
inputMsg={inputMsg}
|
inputMsg={inputMsg}
|
||||||
chatId={currentConversation?.chatId}
|
chatId={currentConversation?.chatId}
|
||||||
domains={domains}
|
models={models}
|
||||||
currentDomain={currentDomain}
|
currentModel={currentModel}
|
||||||
defaultEntity={defaultEntity}
|
defaultEntity={defaultEntity}
|
||||||
collapsed={conversationCollapsed}
|
collapsed={conversationCollapsed}
|
||||||
isCopilotMode={isCopilotMode}
|
isCopilotMode={isCopilotMode}
|
||||||
copilotFullscreen={copilotFullscreen}
|
copilotFullscreen={copilotFullscreen}
|
||||||
onToggleCollapseBtn={onToggleCollapseBtn}
|
onToggleCollapseBtn={onToggleCollapseBtn}
|
||||||
onInputMsgChange={onInputMsgChange}
|
onInputMsgChange={onInputMsgChange}
|
||||||
onSendMsg={(msg: string, domainId?: number) => {
|
onSendMsg={(msg: string, modelId?: number) => {
|
||||||
onSendMsg(msg, messageList, domainId);
|
onSendMsg(msg, messageList, modelId);
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
inputBlur();
|
inputBlur();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onAddConversation={onAddConversation}
|
onAddConversation={onAddConversation}
|
||||||
onCancelDefaultFilter={() => {
|
onCancelDefaultFilter={() => {
|
||||||
changeDomain(undefined);
|
changeModel(undefined);
|
||||||
|
setDefaultEntity(undefined);
|
||||||
if (onCancelCopilotFilter) {
|
if (onCancelCopilotFilter) {
|
||||||
onCancelCopilotFilter();
|
onCancelCopilotFilter();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { request } from 'umi';
|
import { request } from 'umi';
|
||||||
import { DomainType } from './type';
|
import { ModelType } from './type';
|
||||||
|
|
||||||
const prefix = '/api';
|
const prefix = '/api';
|
||||||
|
|
||||||
@@ -24,9 +24,9 @@ export function getAllConversations() {
|
|||||||
return request<Result<any>>(`${prefix}/chat/manage/getAll`);
|
return request<Result<any>>(`${prefix}/chat/manage/getAll`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMiniProgramList(entityId: string, domainId: number) {
|
export function getMiniProgramList(entityId: string, modelId: number) {
|
||||||
return request<Result<any>>(
|
return request<Result<any>>(
|
||||||
`${prefix}/chat/plugin/extend/getAvailablePlugin/${entityId}/${domainId}`,
|
`${prefix}/chat/plugin/extend/getAvailablePlugin/${entityId}/${modelId}`,
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
skipErrorHandler: true,
|
skipErrorHandler: true,
|
||||||
@@ -34,8 +34,8 @@ export function getMiniProgramList(entityId: string, domainId: number) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDomainList() {
|
export function getModelList() {
|
||||||
return request<Result<DomainType[]>>(`${prefix}/chat/conf/domainList/view`, {
|
return request<Result<ModelType[]>>(`${prefix}/chat/conf/modelList/view`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -49,14 +49,14 @@ export function updateQAFeedback(questionId: number, score: number) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function queryMetricSuggestion(domainId: number) {
|
export function queryMetricSuggestion(modelId: number) {
|
||||||
return request<Result<any>>(`${prefix}/chat/recommend/metric/${domainId}`, {
|
return request<Result<any>>(`${prefix}/chat/recommend/metric/${modelId}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function querySuggestion(domainId: number) {
|
export function querySuggestion(modelId: number) {
|
||||||
return request<Result<any>>(`${prefix}/chat/recommend/${domainId}`, {
|
return request<Result<any>>(`${prefix}/chat/recommend/${modelId}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -444,9 +444,9 @@
|
|||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.messageItem {
|
// .messageItem {
|
||||||
margin-top: 12px;
|
// margin-top: 12px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.messageTime {
|
.messageTime {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MsgDataType } from 'supersonic-chat-sdk';
|
import { ChatContextType, MsgDataType } from 'supersonic-chat-sdk';
|
||||||
|
|
||||||
export enum MessageTypeEnum {
|
export enum MessageTypeEnum {
|
||||||
TEXT = 'text', // 指标文本
|
TEXT = 'text', // 指标文本
|
||||||
@@ -10,6 +10,7 @@ export enum MessageTypeEnum {
|
|||||||
PLUGIN = 'PLUGIN', // 插件
|
PLUGIN = 'PLUGIN', // 插件
|
||||||
WEB_PAGE = 'WEB_PAGE', // 插件
|
WEB_PAGE = 'WEB_PAGE', // 插件
|
||||||
RECOMMEND_QUESTIONS = 'recommend_questions', // 推荐问题
|
RECOMMEND_QUESTIONS = 'recommend_questions', // 推荐问题
|
||||||
|
PARSE_OPTIONS = 'parse_options', // 解析选项
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MessageItem = {
|
export type MessageItem = {
|
||||||
@@ -18,13 +19,14 @@ export type MessageItem = {
|
|||||||
msg?: string;
|
msg?: string;
|
||||||
msgValue?: string;
|
msgValue?: string;
|
||||||
identityMsg?: string;
|
identityMsg?: string;
|
||||||
domainId?: number;
|
modelId?: number;
|
||||||
entityId?: string;
|
entityId?: string;
|
||||||
msgData?: MsgDataType;
|
msgData?: MsgDataType;
|
||||||
quote?: string;
|
quote?: string;
|
||||||
score?: number;
|
score?: number;
|
||||||
feedback?: string;
|
feedback?: string;
|
||||||
isHistory?: boolean;
|
isHistory?: boolean;
|
||||||
|
parseOptions?: ChatContextType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ConversationDetailType = {
|
export type ConversationDetailType = {
|
||||||
@@ -35,7 +37,7 @@ export type ConversationDetailType = {
|
|||||||
lastQuestion?: string;
|
lastQuestion?: string;
|
||||||
lastTime?: string;
|
lastTime?: string;
|
||||||
initMsg?: string;
|
initMsg?: string;
|
||||||
domainId?: number;
|
modelId?: number;
|
||||||
entityId?: string;
|
entityId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,7 +45,7 @@ export enum MessageModeEnum {
|
|||||||
INTERPRET = 'interpret',
|
INTERPRET = 'interpret',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DomainType = {
|
export type ModelType = {
|
||||||
id: number;
|
id: number;
|
||||||
parentId: number;
|
parentId: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -66,12 +68,12 @@ export type PluginType = {
|
|||||||
export type DefaultEntityType = {
|
export type DefaultEntityType = {
|
||||||
entityId: string;
|
entityId: string;
|
||||||
entityName: string;
|
entityName: string;
|
||||||
domainName?: string;
|
modelName?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SuggestionItemType = {
|
export type SuggestionItemType = {
|
||||||
id: number;
|
id: number;
|
||||||
domain: number;
|
model: number;
|
||||||
name: string;
|
name: string;
|
||||||
bizName: string;
|
bizName: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Modal, Select, Form, Input, InputNumber, message, Button, Radio } from 'antd';
|
import { Modal, Select, Form, Input, InputNumber, message, Button, Radio } from 'antd';
|
||||||
import { getDimensionList, getDomainList, savePlugin } from './service';
|
import { getDimensionList, getModelList, savePlugin } from './service';
|
||||||
import {
|
import {
|
||||||
DimensionType,
|
DimensionType,
|
||||||
DomainType,
|
ModelType,
|
||||||
ParamTypeEnum,
|
ParamTypeEnum,
|
||||||
ParseModeEnum,
|
ParseModeEnum,
|
||||||
PluginType,
|
PluginType,
|
||||||
@@ -26,10 +26,8 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
||||||
const [domainList, setDomainList] = useState<DomainType[]>([]);
|
const [modelList, setModelList] = useState<ModelType[]>([]);
|
||||||
const [domainDimensionList, setDomainDimensionList] = useState<Record<number, DimensionType[]>>(
|
const [modelDimensionList, setModelDimensionList] = useState<Record<number, DimensionType[]>>({});
|
||||||
{},
|
|
||||||
);
|
|
||||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||||
const [pluginType, setPluginType] = useState<PluginTypeEnum>();
|
const [pluginType, setPluginType] = useState<PluginTypeEnum>();
|
||||||
const [functionName, setFunctionName] = useState<string>();
|
const [functionName, setFunctionName] = useState<string>();
|
||||||
@@ -38,28 +36,25 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
const [filters, setFilters] = useState<any[]>([]);
|
const [filters, setFilters] = useState<any[]>([]);
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
const initDomainList = async () => {
|
const initModelList = async () => {
|
||||||
const res = await getDomainList();
|
const res = await getModelList();
|
||||||
setDomainList([{ id: -1, name: '全部' }, ...getLeafList(res.data)]);
|
setModelList([{ id: -1, name: '默认' }, ...getLeafList(res.data)]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initDomainList();
|
initModelList();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const initDomainDimensions = async (params: any) => {
|
const initModelDimensions = async (params: any) => {
|
||||||
const domainIds = params
|
const modelIds = params
|
||||||
.filter((param: any) => !!param.domainId)
|
.filter((param: any) => !!param.modelId)
|
||||||
.map((param: any) => param.domainId);
|
.map((param: any) => param.modelId);
|
||||||
const res = await Promise.all(domainIds.map((domainId: number) => getDimensionList(domainId)));
|
const res = await Promise.all(modelIds.map((modelId: number) => getDimensionList(modelId)));
|
||||||
setDomainDimensionList(
|
setModelDimensionList(
|
||||||
domainIds.reduce(
|
modelIds.reduce((result: Record<number, DimensionType[]>, modelId: number, index: number) => {
|
||||||
(result: Record<number, DimensionType[]>, domainId: number, index: number) => {
|
result[modelId] = res[index].data.list;
|
||||||
result[domainId] = res[index].data.list;
|
return result;
|
||||||
return result;
|
}, {}),
|
||||||
},
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,7 +74,7 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
(option: any) => option.paramType !== ParamTypeEnum.FORWARD,
|
(option: any) => option.paramType !== ParamTypeEnum.FORWARD,
|
||||||
);
|
);
|
||||||
setFilters(params);
|
setFilters(params);
|
||||||
initDomainDimensions(params);
|
initModelDimensions(params);
|
||||||
}
|
}
|
||||||
setPluginType(detail.type);
|
setPluginType(detail.type);
|
||||||
const parseModeObj = JSON.parse(detail.parseModeConfig || '{}');
|
const parseModeObj = JSON.parse(detail.parseModeConfig || '{}');
|
||||||
@@ -159,7 +154,7 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
await savePlugin({
|
await savePlugin({
|
||||||
...values,
|
...values,
|
||||||
id: detail?.id,
|
id: detail?.id,
|
||||||
domainList: isArray(values.domainList) ? values.domainList : [values.domainList],
|
modelList: isArray(values.modelList) ? values.modelList : [values.modelList],
|
||||||
config: JSON.stringify(config),
|
config: JSON.stringify(config),
|
||||||
parseModeConfig: JSON.stringify(getFunctionParam(values.pattern)),
|
parseModeConfig: JSON.stringify(getFunctionParam(values.pattern)),
|
||||||
});
|
});
|
||||||
@@ -169,11 +164,11 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateDimensionList = async (value: number) => {
|
const updateDimensionList = async (value: number) => {
|
||||||
if (domainDimensionList[value]) {
|
if (modelDimensionList[value]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const res = await getDimensionList(value);
|
const res = await getDimensionList(value);
|
||||||
setDomainDimensionList({ ...domainDimensionList, [value]: res.data.list });
|
setModelDimensionList({ ...modelDimensionList, [value]: res.data.list });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -186,12 +181,12 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
>
|
>
|
||||||
<Form {...layout} form={form} style={{ maxWidth: 820 }}>
|
<Form {...layout} form={form} style={{ maxWidth: 820 }}>
|
||||||
<FormItem name="domainList" label="主题域">
|
<FormItem name="modelList" label="主题域">
|
||||||
<Select
|
<Select
|
||||||
placeholder="请选择主题域"
|
placeholder="请选择主题域"
|
||||||
options={domainList.map((domain) => ({
|
options={modelList.map((model) => ({
|
||||||
label: domain.name,
|
label: model.name,
|
||||||
value: domain.id,
|
value: model.id,
|
||||||
}))}
|
}))}
|
||||||
showSearch
|
showSearch
|
||||||
filterOption={(input, option) =>
|
filterOption={(input, option) =>
|
||||||
@@ -223,7 +218,6 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
setPluginType(value);
|
setPluginType(value);
|
||||||
if (value === PluginTypeEnum.DSL) {
|
if (value === PluginTypeEnum.DSL) {
|
||||||
form.setFieldsValue({ parseMode: ParseModeEnum.FUNCTION_CALL });
|
form.setFieldsValue({ parseMode: ParseModeEnum.FUNCTION_CALL });
|
||||||
// setFunctionName('DSL');
|
|
||||||
setFunctionParams([
|
setFunctionParams([
|
||||||
{
|
{
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
@@ -236,47 +230,6 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
|
||||||
name="pattern"
|
|
||||||
label="插件描述"
|
|
||||||
rules={[{ required: true, message: '请输入插件描述' }]}
|
|
||||||
>
|
|
||||||
<TextArea placeholder="请输入插件描述,多个描述换行分隔" allowClear />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem name="exampleQuestions" label="示例问题">
|
|
||||||
<div className={styles.paramsSection}>
|
|
||||||
{examples.map((example) => {
|
|
||||||
const { id, question } = example;
|
|
||||||
return (
|
|
||||||
<div className={styles.filterRow} key={id}>
|
|
||||||
<Input
|
|
||||||
placeholder="示例问题"
|
|
||||||
value={question}
|
|
||||||
className={styles.questionExample}
|
|
||||||
onChange={(e) => {
|
|
||||||
example.question = e.target.value;
|
|
||||||
setExamples([...examples]);
|
|
||||||
}}
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
<DeleteOutlined
|
|
||||||
onClick={() => {
|
|
||||||
setExamples(examples.filter((item) => item.id !== id));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setExamples([...examples, { id: uuid() }]);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PlusOutlined />
|
|
||||||
新增示例问题
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem label="函数名称">
|
<FormItem label="函数名称">
|
||||||
<Input
|
<Input
|
||||||
value={functionName}
|
value={functionName}
|
||||||
@@ -287,6 +240,9 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
allowClear
|
allowClear
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem name="pattern" label="函数描述">
|
||||||
|
<TextArea placeholder="请输入函数描述,多个描述换行分隔" allowClear />
|
||||||
|
</FormItem>
|
||||||
<FormItem name="params" label="函数参数" hidden={pluginType === PluginTypeEnum.DSL}>
|
<FormItem name="params" label="函数参数" hidden={pluginType === PluginTypeEnum.DSL}>
|
||||||
<div className={styles.paramsSection}>
|
<div className={styles.paramsSection}>
|
||||||
{functionParams.map((functionParam: FunctionParamFormItemType) => {
|
{functionParams.map((functionParam: FunctionParamFormItemType) => {
|
||||||
@@ -345,6 +301,40 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem name="exampleQuestions" label="示例问题">
|
||||||
|
<div className={styles.paramsSection}>
|
||||||
|
{examples.map((example) => {
|
||||||
|
const { id, question } = example;
|
||||||
|
return (
|
||||||
|
<div className={styles.filterRow} key={id}>
|
||||||
|
<Input
|
||||||
|
placeholder="示例问题"
|
||||||
|
value={question}
|
||||||
|
className={styles.questionExample}
|
||||||
|
onChange={(e) => {
|
||||||
|
example.question = e.target.value;
|
||||||
|
setExamples([...examples]);
|
||||||
|
}}
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
<DeleteOutlined
|
||||||
|
onClick={() => {
|
||||||
|
setExamples(examples.filter((item) => item.id !== id));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setExamples([...examples, { id: uuid() }]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusOutlined />
|
||||||
|
新增示例问题
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
{(pluginType === PluginTypeEnum.WEB_PAGE || pluginType === PluginTypeEnum.WEB_SERVICE) && (
|
{(pluginType === PluginTypeEnum.WEB_PAGE || pluginType === PluginTypeEnum.WEB_SERVICE) && (
|
||||||
<>
|
<>
|
||||||
<FormItem name="url" label="地址" rules={[{ required: true, message: '请输入地址' }]}>
|
<FormItem name="url" label="地址" rules={[{ required: true, message: '请输入地址' }]}>
|
||||||
@@ -391,9 +381,9 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
<>
|
<>
|
||||||
<Select
|
<Select
|
||||||
placeholder="主题域"
|
placeholder="主题域"
|
||||||
options={domainList.map((domain) => ({
|
options={modelList.map((model) => ({
|
||||||
label: domain.name,
|
label: model.name,
|
||||||
value: domain.id,
|
value: model.id,
|
||||||
}))}
|
}))}
|
||||||
showSearch
|
showSearch
|
||||||
filterOption={(input, option) =>
|
filterOption={(input, option) =>
|
||||||
@@ -403,16 +393,16 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
|||||||
}
|
}
|
||||||
className={styles.filterParamName}
|
className={styles.filterParamName}
|
||||||
allowClear
|
allowClear
|
||||||
value={filter.domainId}
|
value={filter.modelId}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
filter.domainId = value;
|
filter.modelId = value;
|
||||||
setFilters([...filters]);
|
setFilters([...filters]);
|
||||||
updateDimensionList(value);
|
updateDimensionList(value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
placeholder="请选择维度,需先选择主题域"
|
placeholder="请选择维度,需先选择主题域"
|
||||||
options={(domainDimensionList[filter.domainId] || []).map(
|
options={(modelDimensionList[filter.modelId] || []).map(
|
||||||
(dimension) => ({
|
(dimension) => ({
|
||||||
label: dimension.name,
|
label: dimension.name,
|
||||||
value: `${dimension.id}`,
|
value: `${dimension.id}`,
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import moment from 'moment';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { PARSE_MODE_MAP, PLUGIN_TYPE_MAP } from './constants';
|
import { PARSE_MODE_MAP, PLUGIN_TYPE_MAP } from './constants';
|
||||||
import DetailModal from './DetailModal';
|
import DetailModal from './DetailModal';
|
||||||
import { deletePlugin, getDomainList, getPluginList } from './service';
|
import { deletePlugin, getModelList, getPluginList } from './service';
|
||||||
import styles from './style.less';
|
import styles from './style.less';
|
||||||
import { DomainType, ParseModeEnum, PluginType, PluginTypeEnum } from './type';
|
import { ModelType, ParseModeEnum, PluginType, PluginTypeEnum } from './type';
|
||||||
|
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
|
|
||||||
@@ -15,27 +15,27 @@ const PluginManage = () => {
|
|||||||
const [name, setName] = useState<string>();
|
const [name, setName] = useState<string>();
|
||||||
const [type, setType] = useState<PluginTypeEnum>();
|
const [type, setType] = useState<PluginTypeEnum>();
|
||||||
const [pattern, setPattern] = useState<string>();
|
const [pattern, setPattern] = useState<string>();
|
||||||
const [domain, setDomain] = useState<string>();
|
const [model, setModel] = useState<string>();
|
||||||
const [data, setData] = useState<PluginType[]>([]);
|
const [data, setData] = useState<PluginType[]>([]);
|
||||||
const [domainList, setDomainList] = useState<DomainType[]>([]);
|
const [modelList, setModelList] = useState<ModelType[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [currentPluginDetail, setCurrentPluginDetail] = useState<PluginType>();
|
const [currentPluginDetail, setCurrentPluginDetail] = useState<PluginType>();
|
||||||
const [detailModalVisible, setDetailModalVisible] = useState(false);
|
const [detailModalVisible, setDetailModalVisible] = useState(false);
|
||||||
|
|
||||||
const initDomainList = async () => {
|
const initModelList = async () => {
|
||||||
const res = await getDomainList();
|
const res = await getModelList();
|
||||||
setDomainList(getLeafList(res.data));
|
setModelList(getLeafList(res.data));
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateData = async (filters?: any) => {
|
const updateData = async (filters?: any) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const res = await getPluginList({ name, type, pattern, domain, ...(filters || {}) });
|
const res = await getPluginList({ name, type, pattern, model, ...(filters || {}) });
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setData(res.data.map((item) => ({ ...item, config: JSON.parse(item.config || '{}') })));
|
setData(res.data?.map((item) => ({ ...item, config: JSON.parse(item.config || '{}') })) || []);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initDomainList();
|
initModelList();
|
||||||
updateData();
|
updateData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -58,17 +58,17 @@ const PluginManage = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '主题域',
|
title: '主题域',
|
||||||
dataIndex: 'domainList',
|
dataIndex: 'modelList',
|
||||||
key: 'domainList',
|
key: 'modelList',
|
||||||
width: 200,
|
width: 200,
|
||||||
render: (value: number[]) => {
|
render: (value: number[]) => {
|
||||||
if (value?.includes(-1)) {
|
if (value?.includes(-1)) {
|
||||||
return '全部';
|
return '默认';
|
||||||
}
|
}
|
||||||
return value ? (
|
return value ? (
|
||||||
<div className={styles.domainColumn}>
|
<div className={styles.modelColumn}>
|
||||||
{value.map((id, index) => {
|
{value.map((id, index) => {
|
||||||
const name = domainList.find((domain) => domain.id === +id)?.name;
|
const name = modelList.find((model) => model.id === +id)?.name;
|
||||||
return name ? <Tag key={id}>{name}</Tag> : null;
|
return name ? <Tag key={id}>{name}</Tag> : null;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
@@ -90,7 +90,7 @@ const PluginManage = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '插件描述',
|
title: '函数描述',
|
||||||
dataIndex: 'pattern',
|
dataIndex: 'pattern',
|
||||||
key: 'pattern',
|
key: 'pattern',
|
||||||
width: 450,
|
width: 450,
|
||||||
@@ -139,9 +139,9 @@ const PluginManage = () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const onDomainChange = (value: string) => {
|
const onModelChange = (value: string) => {
|
||||||
setDomain(value);
|
setModel(value);
|
||||||
updateData({ domain: value });
|
updateData({ model: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTypeChange = (value: PluginTypeEnum) => {
|
const onTypeChange = (value: PluginTypeEnum) => {
|
||||||
@@ -171,10 +171,10 @@ const PluginManage = () => {
|
|||||||
<Select
|
<Select
|
||||||
className={styles.filterItemControl}
|
className={styles.filterItemControl}
|
||||||
placeholder="请选择主题域"
|
placeholder="请选择主题域"
|
||||||
options={domainList.map((domain) => ({ label: domain.name, value: domain.id }))}
|
options={modelList.map((model) => ({ label: model.name, value: model.id }))}
|
||||||
value={domain}
|
value={model}
|
||||||
allowClear
|
allowClear
|
||||||
onChange={onDomainChange}
|
onChange={onModelChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.filterItem}>
|
<div className={styles.filterItem}>
|
||||||
@@ -190,10 +190,10 @@ const PluginManage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.filterItem}>
|
<div className={styles.filterItem}>
|
||||||
<div className={styles.filterItemTitle}>插件描述</div>
|
<div className={styles.filterItemTitle}>函数描述</div>
|
||||||
<Search
|
<Search
|
||||||
className={styles.filterItemControl}
|
className={styles.filterItemControl}
|
||||||
placeholder="请输入插件描述"
|
placeholder="请输入函数描述"
|
||||||
value={pattern}
|
value={pattern}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setPattern(e.target.value);
|
setPattern(e.target.value);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { request } from "umi";
|
import { request } from "umi";
|
||||||
import { DimensionType, DomainType, PluginType } from "./type";
|
import { DimensionType, ModelType, PluginType } from "./type";
|
||||||
|
|
||||||
export function savePlugin(params: Partial<PluginType>) {
|
export function savePlugin(params: Partial<PluginType>) {
|
||||||
return request<Result<any>>('/api/chat/plugin', {
|
return request<Result<any>>('/api/chat/plugin', {
|
||||||
@@ -21,17 +21,17 @@ export function deletePlugin(id: number) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDomainList() {
|
export function getModelList() {
|
||||||
return request<Result<DomainType[]>>('/api/chat/conf/domainList', {
|
return request<Result<ModelType[]>>('/api/chat/conf/modelList', {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDimensionList(domainId: number) {
|
export function getDimensionList(modelId: number) {
|
||||||
return request<Result<{list: DimensionType[]}>>('/api/semantic/dimension/queryDimension', {
|
return request<Result<{list: DimensionType[]}>>('/api/semantic/dimension/queryDimension', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
domainIds: [domainId],
|
modelIds: [modelId],
|
||||||
current: 1,
|
current: 1,
|
||||||
pageSize: 2000
|
pageSize: 2000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.domainColumn {
|
.modelColumn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
column-gap: 2px;
|
column-gap: 2px;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export enum ParamTypeEnum {
|
|||||||
export type PluginType = {
|
export type PluginType = {
|
||||||
id: number;
|
id: number;
|
||||||
type: PluginTypeEnum;
|
type: PluginTypeEnum;
|
||||||
domainList: number[];
|
modelList: number[];
|
||||||
pattern: string;
|
pattern: string;
|
||||||
parseMode: ParseModeEnum;
|
parseMode: ParseModeEnum;
|
||||||
parseModeConfig: string;
|
parseModeConfig: string;
|
||||||
@@ -34,7 +34,7 @@ export type PluginType = {
|
|||||||
config: PluginConfigType;
|
config: PluginConfigType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DomainType = {
|
export type ModelType = {
|
||||||
id: number | string;
|
id: number | string;
|
||||||
parentId: number;
|
parentId: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
160
webapp/packages/supersonic-fe/src/pages/Copilot/index.tsx
Normal file
160
webapp/packages/supersonic-fe/src/pages/Copilot/index.tsx
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
import IconFont from '@/components/IconFont';
|
||||||
|
import {
|
||||||
|
CaretRightOutlined,
|
||||||
|
CloseOutlined,
|
||||||
|
FullscreenExitOutlined,
|
||||||
|
FullscreenOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import Chat from '../Chat';
|
||||||
|
import { ModelType } from '../Chat/type';
|
||||||
|
import styles from './style.less';
|
||||||
|
import { useDispatch } from 'umi';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
copilotSendMsg: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Copilot: React.FC<Props> = ({ copilotSendMsg }) => {
|
||||||
|
const [chatVisible, setChatVisible] = useState(false);
|
||||||
|
const [defaultModelName, setDefaultModelName] = useState('');
|
||||||
|
const [fullscreen, setFullscreen] = useState(false);
|
||||||
|
const [triggerNewConversation, setTriggerNewConversation] = useState(false);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const chatVisibleValue = localStorage.getItem('CHAT_VISIBLE') === 'true';
|
||||||
|
if (chatVisibleValue) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setChatVisible(true);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (copilotSendMsg) {
|
||||||
|
updateChatVisible(true);
|
||||||
|
setTriggerNewConversation(true);
|
||||||
|
}
|
||||||
|
}, [copilotSendMsg]);
|
||||||
|
|
||||||
|
const updateChatVisible = (visible: boolean) => {
|
||||||
|
setChatVisible(visible);
|
||||||
|
localStorage.setItem('CHAT_VISIBLE', visible ? 'true' : 'false');
|
||||||
|
};
|
||||||
|
|
||||||
|
const onToggleChatVisible = () => {
|
||||||
|
const chatVisibleValue = !chatVisible;
|
||||||
|
updateChatVisible(chatVisibleValue);
|
||||||
|
if (!chatVisibleValue) {
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
} else {
|
||||||
|
if (fullscreen) {
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCloseChat = () => {
|
||||||
|
updateChatVisible(false);
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTransferChat = () => {
|
||||||
|
window.open(
|
||||||
|
`${window.location.href.includes('webapp') ? '/webapp' : ''}/chat?cid=${localStorage.getItem(
|
||||||
|
'CONVERSATION_ID',
|
||||||
|
)}${defaultModelName ? `&modelName=${defaultModelName}` : ''}`,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCurrentModelChange = (model?: ModelType) => {
|
||||||
|
setDefaultModelName(model?.name || '');
|
||||||
|
if (model?.name !== defaultModelName) {
|
||||||
|
onCancelCopilotFilter();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEnterFullscreen = () => {
|
||||||
|
setFullscreen(true);
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
};
|
||||||
|
|
||||||
|
const onExitFullscreen = () => {
|
||||||
|
setFullscreen(false);
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCheckMoreDetail = () => {
|
||||||
|
if (!fullscreen) {
|
||||||
|
onEnterFullscreen();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancelCopilotFilter = () => {
|
||||||
|
dispatch({
|
||||||
|
type: 'globalState/setGlobalCopilotFilter',
|
||||||
|
payload: undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onNewConversationTriggered = () => {
|
||||||
|
setTriggerNewConversation(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const chatPopoverClass = classNames(styles.chatPopover, {
|
||||||
|
[styles.fullscreen]: fullscreen,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={styles.copilot} onClick={onToggleChatVisible}>
|
||||||
|
<IconFont type="icon-copilot-fill" />
|
||||||
|
</div>
|
||||||
|
{chatVisible && (
|
||||||
|
<div className={styles.copilotContent}>
|
||||||
|
<div className={chatPopoverClass}>
|
||||||
|
<div className={styles.header}>
|
||||||
|
<div className={styles.leftSection}>
|
||||||
|
<CloseOutlined className={styles.close} onClick={onCloseChat} />
|
||||||
|
{fullscreen ? (
|
||||||
|
<FullscreenExitOutlined
|
||||||
|
className={styles.fullscreen}
|
||||||
|
onClick={onExitFullscreen}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FullscreenOutlined className={styles.fullscreen} onClick={onEnterFullscreen} />
|
||||||
|
)}
|
||||||
|
<IconFont
|
||||||
|
type="icon-weibiaoti-"
|
||||||
|
className={styles.transfer}
|
||||||
|
onClick={onTransferChat}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.title}>Copilot</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.chat}>
|
||||||
|
<Chat
|
||||||
|
defaultModelName={defaultModelName}
|
||||||
|
copilotSendMsg={copilotSendMsg}
|
||||||
|
isCopilotMode
|
||||||
|
copilotFullscreen={fullscreen}
|
||||||
|
triggerNewConversation={triggerNewConversation}
|
||||||
|
onNewConversationTriggered={onNewConversationTriggered}
|
||||||
|
onCurrentModelChange={onCurrentModelChange}
|
||||||
|
onCancelCopilotFilter={onCancelCopilotFilter}
|
||||||
|
onCheckMoreDetail={onCheckMoreDetail}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CaretRightOutlined className={styles.rightArrow} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Copilot;
|
||||||
110
webapp/packages/supersonic-fe/src/pages/Copilot/style.less
Normal file
110
webapp/packages/supersonic-fe/src/pages/Copilot/style.less
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
.copilot {
|
||||||
|
position: fixed;
|
||||||
|
right: 8px;
|
||||||
|
bottom: 220px;
|
||||||
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 26px;
|
||||||
|
background-color: var(--chat-blue);
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 8px 8px 20px 0 rgba(55, 99, 170, 0.1);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
box-shadow: 8px 8px 20px rgba(55, 99, 170, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatPopover {
|
||||||
|
position: fixed;
|
||||||
|
right: 90px;
|
||||||
|
bottom: 5vh;
|
||||||
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 50vw;
|
||||||
|
height: 90vh;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 8px 8px 20px rgba(55, 99, 170, 0.1), -2px -2px 16px rgba(55, 99, 170, 0.1);
|
||||||
|
transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out,
|
||||||
|
-webkit-transform 0.3s ease-in-out;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
position: relative;
|
||||||
|
z-index: 99;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 50px;
|
||||||
|
padding-right: 16px;
|
||||||
|
padding-left: 16px;
|
||||||
|
background: linear-gradient(90deg, #4692ff 0%, #1877ff 98%);
|
||||||
|
box-shadow: 1px 1px 8px #1b4aef5c;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftSection {
|
||||||
|
position: absolute;
|
||||||
|
left: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
column-gap: 20px;
|
||||||
|
|
||||||
|
.close {
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullscreen {
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat {
|
||||||
|
height: calc(90vh - 50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fullscreen {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100vw - 90px);
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
.chat {
|
||||||
|
height: calc(100vh - 50px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightArrow {
|
||||||
|
position: fixed;
|
||||||
|
right: 69px;
|
||||||
|
bottom: 232px;
|
||||||
|
z-index: 999;
|
||||||
|
color: var(--chat-blue);
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"suppressImplicitAnyIndexErrors": true,
|
"ignoreDeprecations": "5.0",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"],
|
"@/*": ["./src/*"],
|
||||||
|
|||||||
Reference in New Issue
Block a user