[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.

Co-authored-by: tristanliu <tristanliu@tencent.com>
This commit is contained in:
tristanliu
2023-07-21 15:30:38 +08:00
committed by GitHub
parent 6492316e23
commit 078a81038f
39 changed files with 1541 additions and 1161 deletions

View File

@@ -1,139 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Button, Modal, message } from 'antd';
import { addDomainExtend, editDomainExtend, getDomainExtendDetailConfig } from '../../service';
import DimensionMetricVisibleTransfer from './DimensionMetricVisibleTransfer';
import { exChangeRichEntityListToIds } from './utils';
type Props = {
domainId: number;
themeData: any;
settingType: 'dimension' | 'metric';
settingSourceList: any[];
onCancel: () => void;
visible: boolean;
onSubmit: (params?: any) => void;
};
const dimensionConfig = {
blackIdListKey: 'blackDimIdList',
visibleIdListKey: 'whiteDimIdList',
modalTitle: '问答可见维度信息',
titles: ['不可见维度', '可见维度'],
};
const metricConfig = {
blackIdListKey: 'blackMetricIdList',
visibleIdListKey: 'whiteMetricIdList',
modalTitle: '问答可见指标信息',
titles: ['不可见指标', '可见指标'],
};
const DimensionMetricVisibleModal: React.FC<Props> = ({
domainId,
visible,
themeData = {},
settingType,
settingSourceList,
onCancel,
onSubmit,
}) => {
const [sourceList, setSourceList] = useState<any[]>([]);
const [selectedKeyList, setSelectedKeyList] = useState<string[]>([]);
const settingTypeConfig = settingType === 'dimension' ? dimensionConfig : metricConfig;
useEffect(() => {
const list = settingSourceList.map((item: any) => {
const { id, name } = item;
return { id, name, type: settingType };
});
setSourceList(list);
}, [settingSourceList]);
useEffect(() => {
setSelectedKeyList(themeData.visibility?.[settingTypeConfig.visibleIdListKey] || []);
}, [themeData]);
const saveEntity = async () => {
const { id, entity } = themeData;
let saveDomainExtendQuery = addDomainExtend;
if (id) {
saveDomainExtendQuery = editDomainExtend;
}
const blackIdList = settingSourceList.reduce((list, item: any) => {
const { id: targetId } = item;
if (!selectedKeyList.includes(targetId)) {
list.push(targetId);
}
return list;
}, []);
const entityParams = exChangeRichEntityListToIds(entity);
themeData.entity = entityParams;
const params = {
...themeData,
visibility: themeData.visibility || {},
};
params.visibility[settingTypeConfig.blackIdListKey] = blackIdList;
if (!params.visibility.blackDimIdList) {
params.visibility.blackDimIdList = [];
}
if (!params.visibility.blackMetricIdList) {
params.visibility.blackMetricIdList = [];
}
const { code, msg } = await saveDomainExtendQuery({
...params,
id,
domainId,
});
if (code === 200) {
onSubmit?.();
message.success('保存成功');
return;
}
message.error(msg);
};
const handleTransferChange = (newTargetKeys: string[]) => {
setSelectedKeyList(newTargetKeys);
};
const renderFooter = () => {
return (
<>
<Button onClick={onCancel}></Button>
<Button
type="primary"
onClick={() => {
saveEntity();
}}
>
</Button>
</>
);
};
return (
<>
<Modal
width={1200}
destroyOnClose
title={settingTypeConfig.modalTitle}
maskClosable={false}
open={visible}
footer={renderFooter()}
onCancel={onCancel}
>
<DimensionMetricVisibleTransfer
titles={settingTypeConfig.titles}
sourceList={sourceList}
targetList={selectedKeyList}
onChange={(newTargetKeys) => {
handleTransferChange(newTargetKeys);
}}
/>
</Modal>
</>
);
};
export default DimensionMetricVisibleModal;

View File

@@ -1,14 +1,14 @@
import { Space, Table, Transfer, Checkbox, Tooltip, Button } from 'antd';
import { Table, Transfer, Checkbox, Button } from 'antd';
import type { ColumnsType, TableRowSelection } from 'antd/es/table/interface';
import type { TransferItem } from 'antd/es/transfer';
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import difference from 'lodash/difference';
import React, { useState } from 'react';
import type { IChatConfig } from '../../data';
import DimensionValueSettingModal from './DimensionValueSettingModal';
import TransTypeTag from '../TransTypeTag';
import { TransType } from '../../enum';
import TableTitleTooltips from '../../components/TableTitleTooltips';
interface RecordType {
id: number;
@@ -72,12 +72,10 @@ const DimensionMetricVisibleTableTransfer: React.FC<Props> = ({
{
dataIndex: 'y',
title: (
<Space>
<span></span>
<Tooltip title="勾选可见后,维度值将在搜索时可以被联想出来">
<ExclamationCircleOutlined />
</Tooltip>
</Space>
<TableTitleTooltips
title="维度值可见"
tooltips="勾选可见后,维度值将在搜索时可以被联想出来"
/>
),
width: 120,
render: (_, record) => {

View File

@@ -1,138 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Button, Modal, message, Space } from 'antd';
import ProCard from '@ant-design/pro-card';
import { addDomainExtend, editDomainExtend } from '../../service';
import DimensionMetricVisibleTransfer from './DimensionMetricVisibleTransfer';
import SqlEditor from '@/components/SqlEditor';
type Props = {
domainId: number;
themeData: any;
settingSourceList: any[];
onCancel: () => void;
visible: boolean;
onSubmit: (params?: any) => void;
};
const DimensionSearchVisibleModal: React.FC<Props> = ({
domainId,
themeData,
visible,
settingSourceList,
onCancel,
onSubmit,
}) => {
const [sourceList, setSourceList] = useState<any[]>([]);
const [selectedKeyList, setSelectedKeyList] = useState<string[]>([]);
const [dictRules, setDictRules] = useState<string>('');
useEffect(() => {
const knowledgeInfos = themeData?.knowledgeInfos;
if (Array.isArray(knowledgeInfos)) {
const target = knowledgeInfos[0];
if (Array.isArray(target?.ruleList)) {
setDictRules(target.ruleList[0]);
}
const selectKeys = knowledgeInfos.map((item: any) => {
return item.itemId;
});
setSelectedKeyList(selectKeys);
}
}, [themeData]);
useEffect(() => {
const list = settingSourceList.map((item: any) => {
const { id, name } = item;
return { id, name, type: 'dimension' };
});
setSourceList(list);
}, [settingSourceList]);
const saveDictBatch = async () => {
const knowledgeInfos = selectedKeyList.map((key: string) => {
return {
itemId: key,
type: 'DIMENSION',
isDictInfo: true,
ruleList: dictRules ? [dictRules] : [],
};
});
const id = themeData?.id;
let saveDomainExtendQuery = addDomainExtend;
if (id) {
saveDomainExtendQuery = editDomainExtend;
}
const { code, msg } = await saveDomainExtendQuery({
knowledgeInfos,
domainId,
id,
});
if (code === 200) {
message.success('保存可见维度值成功');
onSubmit?.();
return;
}
message.error(msg);
};
const saveDictSetting = async () => {
await saveDictBatch();
};
const handleTransferChange = (newTargetKeys: string[]) => {
setSelectedKeyList(newTargetKeys);
};
const renderFooter = () => {
return (
<>
<Button onClick={onCancel}></Button>
<Button
type="primary"
onClick={() => {
saveDictSetting();
}}
>
</Button>
</>
);
};
return (
<>
<Modal
width={1200}
destroyOnClose
title={'可见维度值设置'}
maskClosable={false}
open={visible}
footer={renderFooter()}
onCancel={onCancel}
>
<Space direction="vertical" style={{ width: '100%' }} size={20}>
<ProCard bordered title="可见设置">
<DimensionMetricVisibleTransfer
titles={['不可见维度值', '可见维度值']}
sourceList={sourceList}
targetList={selectedKeyList}
onChange={(newTargetKeys) => {
handleTransferChange(newTargetKeys);
}}
/>
</ProCard>
<ProCard bordered title="维度值过滤">
<SqlEditor
height={'150px'}
value={dictRules}
onChange={(sql: string) => {
setDictRules(sql);
}}
/>
</ProCard>
</Space>
</Modal>
</>
);
};
export default DimensionSearchVisibleModal;

View File

@@ -5,7 +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';
type Props = {
initialValues: any;
@@ -17,7 +17,7 @@ const FormItem = Form.Item;
const EntityCreateForm: ForwardRefRenderFunction<any, Props> = ({ initialValues }, ref) => {
const [form] = Form.useForm();
const exchangeFields = ['blackList', 'whiteList', 'ruleList'];
const exchangeFields = ['blackList', 'whiteList'];
const getFormValidateFields = async () => {
const fields = await form.validateFields();
@@ -69,8 +69,9 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = ({ initialValues
<Input placeholder="多个维度值用英文逗号隔开" />
</FormItem>
<FormItem name="ruleList" label="过滤规则">
<SqlEditor height={'150px'} />
<FormItem name="ruleList">
{/* <SqlEditor height={'150px'} /> */}
<CommonEditList title="过滤规则" />
</FormItem>
</Form>
</>

View File

@@ -1,195 +0,0 @@
import { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
import type { ForwardRefRenderFunction } from 'react';
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
import { formLayout } from '@/components/FormHelper/utils';
import { message, Form, Input, Select, Button, InputNumber } from 'antd';
import { addDomainExtend, editDomainExtend } from '../../service';
import styles from '../style.less';
type Props = {
themeData: any;
metricList: any[];
domainId: number;
onSubmit: (params?: any) => void;
};
const FormItem = Form.Item;
const Option = Select.Option;
const MetricSettingForm: ForwardRefRenderFunction<any, Props> = (
{ metricList, domainId, themeData: uniqueMetricData, onSubmit },
ref,
) => {
const [form] = Form.useForm();
const [metricListOptions, setMetricListOptions] = useState<any>([]);
const [unitState, setUnit] = useState<number | null>();
const [periodState, setPeriod] = useState<string>();
const getFormValidateFields = async () => {
return await form.validateFields();
};
useImperativeHandle(ref, () => ({
getFormValidateFields,
}));
useEffect(() => {
form.resetFields();
setUnit(null);
setPeriod('');
if (Object.keys(uniqueMetricData).length === 0) {
return;
}
const { defaultMetrics = [], id } = uniqueMetricData;
const defaultMetric = defaultMetrics[0];
const recordId = id === -1 ? undefined : id;
if (defaultMetric) {
const { period, unit } = defaultMetric;
setUnit(unit);
setPeriod(period);
form.setFieldsValue({
...defaultMetric,
id: recordId,
});
} else {
form.setFieldsValue({
id: recordId,
});
}
}, [uniqueMetricData]);
useEffect(() => {
const metricOption = metricList.map((item: any) => {
return {
label: item.name,
value: item.id,
};
});
setMetricListOptions(metricOption);
}, [metricList]);
const saveEntity = async () => {
const values = await form.validateFields();
const { id } = values;
let saveDomainExtendQuery = addDomainExtend;
if (id) {
saveDomainExtendQuery = editDomainExtend;
}
const { code, msg, data } = await saveDomainExtendQuery({
defaultMetrics: [{ ...values }],
domainId,
id,
});
if (code === 200) {
form.setFieldValue('id', data);
onSubmit?.();
message.success('保存成功');
return;
}
message.error(msg);
};
return (
<>
<Form
{...formLayout}
form={form}
layout="vertical"
className={styles.form}
initialValues={{
unit: 7,
period: 'DAY',
}}
>
<FormItem hidden={true} name="id" label="ID">
<Input placeholder="id" />
</FormItem>
<FormItem
name={'metricId'}
label={
<FormItemTitle
title={'指标'}
subTitle={'问答搜索结果选择中,如果没有指定指标,将会采用默认指标进行展示'}
/>
}
>
<Select
allowClear
showSearch
style={{ width: '100%' }}
filterOption={(inputValue: string, item: any) => {
const { label } = item;
if (label.includes(inputValue)) {
return true;
}
return false;
}}
placeholder="请选择展示指标信息"
options={metricListOptions}
/>
</FormItem>
<FormItem
label={
<FormItemTitle
title={'时间范围'}
subTitle={'问答搜索结果选择中,如果没有指定时间范围,将会采用默认时间范围'}
/>
}
>
<Input.Group compact>
<span
style={{
display: 'inline-block',
lineHeight: '32px',
marginRight: '8px',
}}
>
</span>
<InputNumber
value={unitState}
style={{ width: '120px' }}
onChange={(value) => {
setUnit(value);
form.setFieldValue('unit', value);
}}
/>
<Select
value={periodState}
style={{ width: '100px' }}
onChange={(value) => {
form.setFieldValue('period', value);
setPeriod(value);
}}
>
<Option value="DAY"></Option>
<Option value="WEEK"></Option>
<Option value="MONTH"></Option>
<Option value="YEAR"></Option>
</Select>
</Input.Group>
</FormItem>
<FormItem name="unit" hidden={true}>
<InputNumber />
</FormItem>
<FormItem name="period" hidden={true}>
<Input />
</FormItem>
<FormItem>
<Button
type="primary"
onClick={() => {
saveEntity();
}}
>
</Button>
</FormItem>
</Form>
</>
);
};
export default forwardRef(MetricSettingForm);