mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-16 15:12:26 +00:00
semantic-fe visual modeling pr (#21)
* [improvement][semantic-fe] Added an editing component to set filtering rules for Q&A. Now, the SQL editor will be accompanied by a list for display and control, to resolve ambiguity when using comma-separated values. [improvement][semantic-fe] Improved validation logic and prompt copywriting for data source/dimension/metric editing and creation. [improvement][semantic-fe] Improved user experience for visual modeling. Now, when using the legend to control the display/hide of data sources and their associated metric dimensions, the canvas will be re-layout based on the activated data source in the legend. * [improvement][semantic-fe] Submitted a new version of the visual modeling tool. [improvement][semantic-fe] Fixed an issue with the initialization of YoY and MoM metrics in Q&A settings. [improvement][semantic-fe] Added a version field to the database settings. [improvement][semantic-fe] 1. Added the ability to set YoY and MoM metrics in Q&A settings.2. Moved dimension value editing from the dimension editing window to the dimension list. --------- Co-authored-by: tristanliu <tristanliu@tencent.com>
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
||||
} from './utils';
|
||||
import styles from '../style.less';
|
||||
import { ISemantic } from '../../data';
|
||||
import { ChatConfigType, TransType } from '../../enum';
|
||||
import { ChatConfigType, TransType, SemanticNodeType } from '../../enum';
|
||||
import TransTypeTag from '../TransTypeTag';
|
||||
|
||||
type Props = {
|
||||
@@ -99,7 +99,7 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
|
||||
name,
|
||||
label: (
|
||||
<>
|
||||
<TransTypeTag type={TransType.DIMENSION} />
|
||||
<TransTypeTag type={SemanticNodeType.DIMENSION} />
|
||||
{name}
|
||||
</>
|
||||
),
|
||||
@@ -115,7 +115,7 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
|
||||
name,
|
||||
label: (
|
||||
<>
|
||||
<TransTypeTag type={TransType.METRIC} />
|
||||
<TransTypeTag type={SemanticNodeType.METRIC} />
|
||||
{name}
|
||||
</>
|
||||
),
|
||||
@@ -198,30 +198,56 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
|
||||
</FormItem>
|
||||
)}
|
||||
{chatConfigType === ChatConfigType.AGG && (
|
||||
<FormItem
|
||||
name="metricIds"
|
||||
label={
|
||||
<FormItemTitle
|
||||
title={'指标'}
|
||||
subTitle={'问答搜索结果选择中,如果没有指定指标,将会采用默认指标进行展示'}
|
||||
<>
|
||||
{/* <FormItem
|
||||
name="metricIds"
|
||||
label={
|
||||
<FormItemTitle
|
||||
title={'指标'}
|
||||
subTitle={'问答搜索结果选择中,如果没有指定指标,将会采用默认指标进行展示'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
filterOption={(inputValue: string, item: any) => {
|
||||
const { label } = item;
|
||||
if (label.includes(inputValue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
placeholder="请选择展示指标信息"
|
||||
options={metricListOptions}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
filterOption={(inputValue: string, item: any) => {
|
||||
const { label } = item;
|
||||
if (label.includes(inputValue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
placeholder="请选择展示指标信息"
|
||||
options={metricListOptions}
|
||||
/>
|
||||
</FormItem>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
name="ratioMetricIds"
|
||||
label={
|
||||
<FormItemTitle
|
||||
title={'同环比指标'}
|
||||
subTitle={'问答搜索含有指定的指标,将会同时计算该指标最后一天的同环比'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
filterOption={(inputValue: string, item: any) => {
|
||||
const { label } = item;
|
||||
if (label.includes(inputValue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
placeholder="请选择同环比指标"
|
||||
options={metricListOptions}
|
||||
/>
|
||||
</FormItem> */}
|
||||
</>
|
||||
)}
|
||||
|
||||
<FormItem
|
||||
|
||||
@@ -15,7 +15,7 @@ type Props = {
|
||||
settingSourceList: any[];
|
||||
onCancel: () => void;
|
||||
visible: boolean;
|
||||
onSubmit: (params?: any) => void;
|
||||
onSubmit: (params?: { isSilenceSubmit?: boolean }) => void;
|
||||
};
|
||||
|
||||
const dimensionConfig = {
|
||||
@@ -72,7 +72,8 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
|
||||
}
|
||||
}, [entityData, settingSourceList]);
|
||||
|
||||
const saveEntity = async () => {
|
||||
const saveEntity = async (submitData: any, isSilenceSubmit = false) => {
|
||||
const { selectedKeyList, knowledgeInfosMap } = submitData;
|
||||
const globalKnowledgeConfigFormFields = await formRef?.current?.getFormValidateFields?.();
|
||||
let globalKnowledgeConfig = entityData.globalKnowledgeConfig;
|
||||
if (globalKnowledgeConfigFormFields) {
|
||||
@@ -127,8 +128,10 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
|
||||
id,
|
||||
});
|
||||
if (code === 200) {
|
||||
onSubmit?.();
|
||||
message.success('保存成功');
|
||||
if (!isSilenceSubmit) {
|
||||
message.success('保存成功');
|
||||
}
|
||||
onSubmit?.({ isSilenceSubmit });
|
||||
return;
|
||||
}
|
||||
message.error(msg);
|
||||
@@ -145,7 +148,7 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
saveEntity();
|
||||
saveEntity({ selectedKeyList, knowledgeInfosMap });
|
||||
}}
|
||||
>
|
||||
完成
|
||||
@@ -160,8 +163,9 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
|
||||
key: 'visibleSetting',
|
||||
children: (
|
||||
<DimensionMetricVisibleTransfer
|
||||
onKnowledgeInfosMapChange={(knowledgeInfosMap) => {
|
||||
setKnowledgeInfosMap(knowledgeInfosMap);
|
||||
onKnowledgeInfosMapChange={(knowledgeInfos) => {
|
||||
setKnowledgeInfosMap(knowledgeInfos);
|
||||
saveEntity({ selectedKeyList, knowledgeInfosMap: knowledgeInfos }, true);
|
||||
}}
|
||||
knowledgeInfosMap={knowledgeInfosMap}
|
||||
titles={settingTypeConfig.titles}
|
||||
@@ -169,6 +173,7 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
|
||||
targetList={selectedKeyList}
|
||||
onChange={(newTargetKeys) => {
|
||||
handleTransferChange(newTargetKeys);
|
||||
saveEntity({ selectedKeyList: newTargetKeys, knowledgeInfosMap }, true);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
@@ -177,10 +182,12 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
|
||||
label: '全局维度值过滤',
|
||||
key: 'dimensionValueFilter',
|
||||
children: (
|
||||
<DimensionValueSettingForm
|
||||
initialValues={globalKnowledgeConfigInitialValues}
|
||||
ref={formRef}
|
||||
/>
|
||||
<div style={{ margin: '0 auto', width: '975px' }}>
|
||||
<DimensionValueSettingForm
|
||||
initialValues={globalKnowledgeConfigInitialValues}
|
||||
ref={formRef}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -74,9 +74,11 @@ const DimensionMetricVisibleForm: ForwardRefRenderFunction<any, Props> = ({
|
||||
onCancel={() => {
|
||||
setDimensionModalVisible(false);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
onSubmit={(params) => {
|
||||
onSubmit?.();
|
||||
setDimensionModalVisible(false);
|
||||
if (!params?.isSilenceSubmit) {
|
||||
setDimensionModalVisible(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -5,8 +5,7 @@ import { Form, Input } from 'antd';
|
||||
import { formLayout } from '@/components/FormHelper/utils';
|
||||
import { isString } from 'lodash';
|
||||
import styles from '../style.less';
|
||||
import CommonEditList from '../../components/CommonEditList/index';
|
||||
import SqlEditor from '@/components/SqlEditor';
|
||||
import CommonEditList from '../../components/CommonEditList';
|
||||
type Props = {
|
||||
initialValues: any;
|
||||
onSubmit?: () => void;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
|
||||
import type { ForwardRefRenderFunction } from 'react';
|
||||
import { message, Form, Input, Select, Button } from 'antd';
|
||||
import { addDomainExtend, editDomainExtend } from '../../service';
|
||||
import type { ISemantic, IChatConfig } from '../../data';
|
||||
import { updateDomain } from '../../service';
|
||||
import type { ISemantic } from '../../data';
|
||||
import { formLayout } from '@/components/FormHelper/utils';
|
||||
import { formatRichEntityDataListToIds } from './utils';
|
||||
import styles from '../style.less';
|
||||
|
||||
type Props = {
|
||||
entityData: IChatConfig.IChatRichConfig;
|
||||
entityData?: { id: number; names: string[] };
|
||||
dimensionList: ISemantic.IDimensionList;
|
||||
domainId: number;
|
||||
onSubmit: () => void;
|
||||
@@ -21,22 +20,20 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
ref,
|
||||
) => {
|
||||
const [form] = Form.useForm();
|
||||
const formatEntityData = formatRichEntityDataListToIds(entityData);
|
||||
const [dimensionListOptions, setDimensionListOptions] = useState<any>([]);
|
||||
|
||||
const getFormValidateFields = async () => {
|
||||
return await form.validateFields();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
if (!entityData?.entity) {
|
||||
if (!entityData) {
|
||||
return;
|
||||
}
|
||||
|
||||
form.setFieldsValue({
|
||||
...formatEntityData.entity,
|
||||
id: formatEntityData.id,
|
||||
...entityData,
|
||||
name: entityData.names.join(','),
|
||||
});
|
||||
}, [entityData]);
|
||||
|
||||
@@ -56,20 +53,14 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
|
||||
const saveEntity = async () => {
|
||||
const values = await form.validateFields();
|
||||
const { id, name } = values;
|
||||
let saveDomainExtendQuery = addDomainExtend;
|
||||
if (id) {
|
||||
saveDomainExtendQuery = editDomainExtend;
|
||||
}
|
||||
const { code, msg, data } = await saveDomainExtendQuery({
|
||||
chatDetailConfig: {
|
||||
...formatEntityData,
|
||||
entity: {
|
||||
...values,
|
||||
names: name.split(','),
|
||||
},
|
||||
const { name } = values;
|
||||
|
||||
const { code, msg, data } = await updateDomain({
|
||||
entity: {
|
||||
...values,
|
||||
names: name.split(','),
|
||||
},
|
||||
id,
|
||||
id: domainId,
|
||||
domainId,
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { connect } from 'umi';
|
||||
import type { StateType } from '../../model';
|
||||
import { getDomainExtendDetailConfig } from '../../service';
|
||||
import ProCard from '@ant-design/pro-card';
|
||||
import EntityCreateForm from './EntityCreateForm';
|
||||
|
||||
import DefaultSettingForm from './DefaultSettingForm';
|
||||
import type { IChatConfig } from '../../data';
|
||||
import DimensionMetricVisibleForm from './DimensionMetricVisibleForm';
|
||||
@@ -26,8 +26,6 @@ const EntitySection: React.FC<Props> = ({
|
||||
|
||||
const [entityData, setentityData] = useState<IChatConfig.IChatRichConfig>();
|
||||
|
||||
const entityCreateRef = useRef<any>({});
|
||||
|
||||
const queryThemeListData: any = async () => {
|
||||
const { code, data } = await getDomainExtendDetailConfig({
|
||||
domainId: selectDomainId,
|
||||
@@ -58,25 +56,6 @@ const EntitySection: React.FC<Props> = ({
|
||||
return (
|
||||
<div style={{ width: 800, margin: '0 auto' }}>
|
||||
<Space direction="vertical" style={{ width: '100%' }} size={20}>
|
||||
{chatConfigType === 'detail' && entityData && (
|
||||
<ProCard title="实体" bordered>
|
||||
<EntityCreateForm
|
||||
ref={entityCreateRef}
|
||||
domainId={Number(selectDomainId)}
|
||||
entityData={entityData}
|
||||
dimensionList={dimensionList.filter((item) => {
|
||||
const blackDimensionList = entityData?.visibility?.blackDimIdList;
|
||||
if (Array.isArray(blackDimensionList)) {
|
||||
return !blackDimensionList.includes(item.id);
|
||||
}
|
||||
return false;
|
||||
})}
|
||||
onSubmit={() => {
|
||||
queryThemeListData();
|
||||
}}
|
||||
/>
|
||||
</ProCard>
|
||||
)}
|
||||
<ProCard bordered title="问答可见">
|
||||
<DimensionMetricVisibleForm
|
||||
chatConfigKey={
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { message, Space } from 'antd';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import type { Dispatch } from 'umi';
|
||||
import { connect } from 'umi';
|
||||
import type { StateType } from '../../model';
|
||||
import { getDomainDetail } from '../../service';
|
||||
import ProCard from '@ant-design/pro-card';
|
||||
import EntityCreateForm from './EntityCreateForm';
|
||||
import type { IChatConfig } from '../../data';
|
||||
|
||||
type Props = {
|
||||
dispatch: Dispatch;
|
||||
domainManger: StateType;
|
||||
};
|
||||
|
||||
const EntitySettingSection: React.FC<Props> = ({ domainManger }) => {
|
||||
const { selectDomainId, dimensionList, metricList } = domainManger;
|
||||
|
||||
const [entityData, setEntityData] = useState<IChatConfig.IChatRichConfig>();
|
||||
|
||||
const entityCreateRef = useRef<any>({});
|
||||
|
||||
const queryDomainData: any = async () => {
|
||||
const { code, data } = await getDomainDetail({
|
||||
domainId: selectDomainId,
|
||||
});
|
||||
|
||||
if (code === 200) {
|
||||
const { entity } = data;
|
||||
|
||||
setEntityData(entity);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
message.error('获取问答设置信息失败');
|
||||
};
|
||||
|
||||
const initPage = async () => {
|
||||
queryDomainData();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
initPage();
|
||||
}, [selectDomainId]);
|
||||
|
||||
return (
|
||||
<div style={{ width: 800, margin: '0 auto' }}>
|
||||
<Space direction="vertical" style={{ width: '100%' }} size={20}>
|
||||
{
|
||||
<ProCard title="实体" bordered>
|
||||
<EntityCreateForm
|
||||
ref={entityCreateRef}
|
||||
domainId={Number(selectDomainId)}
|
||||
entityData={entityData}
|
||||
dimensionList={dimensionList}
|
||||
onSubmit={() => {
|
||||
queryDomainData();
|
||||
}}
|
||||
/>
|
||||
</ProCard>
|
||||
}
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default connect(({ domainManger }: { domainManger: StateType }) => ({
|
||||
domainManger,
|
||||
}))(EntitySettingSection);
|
||||
@@ -0,0 +1,87 @@
|
||||
import { message } from 'antd';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { connect } from 'umi';
|
||||
import type { StateType } from '../../model';
|
||||
import { getDomainExtendConfig, addDomainExtend, editDomainExtend } from '../../service';
|
||||
import ProCard from '@ant-design/pro-card';
|
||||
|
||||
import TextAreaCommonEditList from '../../components/CommonEditList/TextArea';
|
||||
|
||||
type Props = {
|
||||
domainManger: StateType;
|
||||
};
|
||||
|
||||
const RecommendedQuestionsSection: React.FC<Props> = ({ domainManger }) => {
|
||||
const { selectDomainId } = domainManger;
|
||||
|
||||
const [questionData, setQuestionData] = useState<string[]>([]);
|
||||
const [currentRecordId, setCurrentRecordId] = useState<number>(0);
|
||||
|
||||
const queryThemeListData: any = async () => {
|
||||
const { code, data } = await getDomainExtendConfig({
|
||||
domainId: selectDomainId,
|
||||
});
|
||||
|
||||
if (code === 200) {
|
||||
const target = data?.[0] || {};
|
||||
if (Array.isArray(target.recommendedQuestions)) {
|
||||
setQuestionData(
|
||||
target.recommendedQuestions.map((item: { question: string }) => {
|
||||
return item.question;
|
||||
}),
|
||||
);
|
||||
setCurrentRecordId(target.id || 0);
|
||||
} else {
|
||||
setQuestionData([]);
|
||||
setCurrentRecordId(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
message.error('获取问答设置信息失败');
|
||||
};
|
||||
|
||||
const saveEntity = async (list: string[]) => {
|
||||
let saveDomainExtendQuery = addDomainExtend;
|
||||
if (currentRecordId) {
|
||||
saveDomainExtendQuery = editDomainExtend;
|
||||
}
|
||||
const { code, msg } = await saveDomainExtendQuery({
|
||||
recommendedQuestions: list.map((question: string) => {
|
||||
return { question };
|
||||
}),
|
||||
id: currentRecordId,
|
||||
domainId: selectDomainId,
|
||||
});
|
||||
|
||||
if (code === 200) {
|
||||
return;
|
||||
}
|
||||
message.error(msg);
|
||||
};
|
||||
|
||||
const initPage = async () => {
|
||||
queryThemeListData();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
initPage();
|
||||
}, [selectDomainId]);
|
||||
|
||||
return (
|
||||
<div style={{ width: 800, margin: '0 auto' }}>
|
||||
<ProCard bordered title="问题推荐列表">
|
||||
<TextAreaCommonEditList
|
||||
value={questionData}
|
||||
onChange={(list) => {
|
||||
saveEntity(list);
|
||||
setQuestionData(list);
|
||||
}}
|
||||
/>
|
||||
</ProCard>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default connect(({ domainManger }: { domainManger: StateType }) => ({
|
||||
domainManger,
|
||||
}))(RecommendedQuestionsSection);
|
||||
@@ -18,8 +18,9 @@ export const formatRichEntityDataListToIds = (
|
||||
const detailData: {
|
||||
dimensionIds: number[];
|
||||
metricIds: number[];
|
||||
} = { dimensionIds: [], metricIds: [] };
|
||||
const { dimensions, metrics } = chatDefaultConfig || {};
|
||||
ratioMetricIds: number[];
|
||||
} = { dimensionIds: [], metricIds: [], ratioMetricIds: [] };
|
||||
const { dimensions, metrics, ratioMetrics } = chatDefaultConfig || {};
|
||||
if (Array.isArray(dimensions)) {
|
||||
detailData.dimensionIds = dimensions.map((item: ISemantic.IDimensionItem) => {
|
||||
return item.id;
|
||||
@@ -30,6 +31,12 @@ export const formatRichEntityDataListToIds = (
|
||||
return item.id;
|
||||
});
|
||||
}
|
||||
if (Array.isArray(ratioMetrics)) {
|
||||
detailData.ratioMetricIds = ratioMetrics.map((item: ISemantic.IMetricItem) => {
|
||||
return item.id;
|
||||
});
|
||||
}
|
||||
|
||||
let entitySetting = {};
|
||||
if (entity) {
|
||||
const entityItem = entity.dimItem;
|
||||
|
||||
Reference in New Issue
Block a user