mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-17 07:32:59 +00:00
[improvement][project] supersonic 0.6.0 version update (#16)
Co-authored-by: lexluo <lexluo@tencent.com>
This commit is contained in:
@@ -5,9 +5,10 @@ const { Meta } = Card;
|
||||
type Props = {
|
||||
open: boolean;
|
||||
onTypeChange: (type: 'fast' | 'normal') => void;
|
||||
onCancel?: () => void;
|
||||
};
|
||||
|
||||
const ClassDataSourceTypeModal: React.FC<Props> = ({ open, onTypeChange }) => {
|
||||
const ClassDataSourceTypeModal: React.FC<Props> = ({ open, onTypeChange, onCancel }) => {
|
||||
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
|
||||
useEffect(() => {
|
||||
setCreateDataSourceModalOpen(open);
|
||||
@@ -19,6 +20,7 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({ open, onTypeChange }) => {
|
||||
open={createDataSourceModalOpen}
|
||||
onCancel={() => {
|
||||
setCreateDataSourceModalOpen(false);
|
||||
onCancel?.();
|
||||
}}
|
||||
footer={null}
|
||||
centered
|
||||
|
||||
@@ -91,6 +91,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
{
|
||||
dataIndex: 'alias',
|
||||
title: '别名',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
dataIndex: 'bizName',
|
||||
|
||||
@@ -71,6 +71,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
{
|
||||
dataIndex: 'alias',
|
||||
title: '别名',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
dataIndex: 'bizName',
|
||||
@@ -91,6 +92,25 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
title: '描述',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
dataIndex: 'type',
|
||||
title: '指标类型',
|
||||
// search: false,
|
||||
valueEnum: {
|
||||
ATOMIC: '原子指标',
|
||||
DERIVED: '衍生指标',
|
||||
},
|
||||
// render: (type: any) => {
|
||||
// switch (type) {
|
||||
// case 'ATOMIC':
|
||||
// return '原子指标';
|
||||
// case 'DERIVED':
|
||||
// return '衍生指标';
|
||||
// default:
|
||||
// return '未知';
|
||||
// }
|
||||
// },
|
||||
},
|
||||
|
||||
{
|
||||
dataIndex: 'updatedAt',
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Form, Input, Modal, Select } from 'antd';
|
||||
import { Button, Form, Input, Modal, Select, List } from 'antd';
|
||||
import { SENSITIVE_LEVEL_OPTIONS } from '../constant';
|
||||
import { formLayout } from '@/components/FormHelper/utils';
|
||||
import SqlEditor from '@/components/SqlEditor';
|
||||
import InfoTagList from './InfoTagList';
|
||||
import { message } from 'antd';
|
||||
|
||||
export type CreateFormProps = {
|
||||
@@ -31,6 +32,7 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
users: [],
|
||||
effectiveTime: 1,
|
||||
});
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const { setFieldsValue } = form;
|
||||
|
||||
@@ -45,6 +47,7 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
};
|
||||
|
||||
const setFormVal = () => {
|
||||
console.log(dimensionItem, 'dimensionItem');
|
||||
setFieldsValue(dimensionItem);
|
||||
};
|
||||
|
||||
@@ -128,6 +131,9 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
))}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem name="defaultValues" label="默认值">
|
||||
<InfoTagList />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
name="description"
|
||||
label="维度描述"
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
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 {
|
||||
formatRichEntityDataListToIds,
|
||||
wrapperTransTypeAndId,
|
||||
splitListToTransTypeId,
|
||||
} from './utils';
|
||||
import styles from '../style.less';
|
||||
import { ISemantic } from '../../data';
|
||||
import { ChatConfigType, TransType } from '../../enum';
|
||||
import TransTypeTag from '../TransTypeTag';
|
||||
|
||||
type Props = {
|
||||
entityData: any;
|
||||
chatConfigKey: string;
|
||||
chatConfigType: ChatConfigType.DETAIL | ChatConfigType.AGG;
|
||||
metricList: ISemantic.IMetricItem[];
|
||||
dimensionList: ISemantic.IDimensionItem[];
|
||||
domainId: number;
|
||||
onSubmit: (params?: any) => void;
|
||||
};
|
||||
|
||||
const FormItem = Form.Item;
|
||||
const Option = Select.Option;
|
||||
|
||||
const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
|
||||
{ metricList, dimensionList, domainId, entityData, chatConfigKey, chatConfigType, onSubmit },
|
||||
ref,
|
||||
) => {
|
||||
const [form] = Form.useForm();
|
||||
const [metricListOptions, setMetricListOptions] = useState<any>([]);
|
||||
const [unitState, setUnit] = useState<number | null>();
|
||||
const [periodState, setPeriod] = useState<string>();
|
||||
const [dataItemListOptions, setDataItemListOptions] = useState<any>([]);
|
||||
const formatEntityData = formatRichEntityDataListToIds(entityData);
|
||||
const getFormValidateFields = async () => {
|
||||
return await form.validateFields();
|
||||
};
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getFormValidateFields,
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
setUnit(null);
|
||||
setPeriod('');
|
||||
if (!entityData?.chatDefaultConfig) {
|
||||
return;
|
||||
}
|
||||
const { chatDefaultConfig, id } = formatEntityData;
|
||||
const { period, unit } = chatDefaultConfig;
|
||||
setUnit(unit);
|
||||
setPeriod(period);
|
||||
form.setFieldsValue({
|
||||
...chatDefaultConfig,
|
||||
id,
|
||||
});
|
||||
if (chatConfigType === ChatConfigType.DETAIL) {
|
||||
initDataItemValue(chatDefaultConfig);
|
||||
}
|
||||
}, [entityData, dataItemListOptions]);
|
||||
|
||||
const initDataItemValue = (chatDefaultConfig: {
|
||||
dimensionIds: number[];
|
||||
metricIds: number[];
|
||||
}) => {
|
||||
const { dimensionIds, metricIds } = chatDefaultConfig;
|
||||
const dimensionIdString = dimensionIds.map((dimensionId: number) => {
|
||||
return wrapperTransTypeAndId(TransType.DIMENSION, dimensionId);
|
||||
});
|
||||
const metricIdString = metricIds.map((metricId: number) => {
|
||||
return wrapperTransTypeAndId(TransType.METRIC, metricId);
|
||||
});
|
||||
form.setFieldsValue({
|
||||
dataItemIds: [...dimensionIdString, ...metricIdString],
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const metricOption = metricList.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
});
|
||||
setMetricListOptions(metricOption);
|
||||
}, [metricList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (Array.isArray(dimensionList) && Array.isArray(metricList)) {
|
||||
const dimensionEnum = dimensionList.map((item: ISemantic.IDimensionItem) => {
|
||||
const { name, id, bizName } = item;
|
||||
return {
|
||||
name,
|
||||
label: (
|
||||
<>
|
||||
<TransTypeTag type={TransType.DIMENSION} />
|
||||
{name}
|
||||
</>
|
||||
),
|
||||
value: wrapperTransTypeAndId(TransType.DIMENSION, id),
|
||||
bizName,
|
||||
id,
|
||||
transType: TransType.DIMENSION,
|
||||
};
|
||||
});
|
||||
const metricEnum = metricList.map((item: ISemantic.IMetricItem) => {
|
||||
const { name, id, bizName } = item;
|
||||
return {
|
||||
name,
|
||||
label: (
|
||||
<>
|
||||
<TransTypeTag type={TransType.METRIC} />
|
||||
{name}
|
||||
</>
|
||||
),
|
||||
value: wrapperTransTypeAndId(TransType.METRIC, id),
|
||||
bizName,
|
||||
id,
|
||||
transType: TransType.METRIC,
|
||||
};
|
||||
});
|
||||
setDataItemListOptions([...dimensionEnum, ...metricEnum]);
|
||||
}
|
||||
}, [dimensionList, metricList]);
|
||||
|
||||
const saveEntity = async () => {
|
||||
const values = await form.validateFields();
|
||||
const { id, dataItemIds } = values;
|
||||
let dimensionConfig = {};
|
||||
if (dataItemIds) {
|
||||
const { dimensionIds, metricIds } = splitListToTransTypeId(dataItemIds);
|
||||
dimensionConfig = {
|
||||
dimensionIds,
|
||||
metricIds,
|
||||
};
|
||||
}
|
||||
let saveDomainExtendQuery = addDomainExtend;
|
||||
if (id) {
|
||||
saveDomainExtendQuery = editDomainExtend;
|
||||
}
|
||||
const params = {
|
||||
...formatEntityData,
|
||||
chatDefaultConfig: { ...values, ...dimensionConfig },
|
||||
};
|
||||
const { code, msg, data } = await saveDomainExtendQuery({
|
||||
[chatConfigKey]: params,
|
||||
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>
|
||||
|
||||
{chatConfigType === ChatConfigType.DETAIL && (
|
||||
<FormItem name="dataItemIds" label="展示维度/指标">
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
optionLabelProp="name"
|
||||
filterOption={(inputValue: string, item: any) => {
|
||||
const { name } = item;
|
||||
if (name.includes(inputValue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
placeholder="请选择展示维度/指标信息"
|
||||
options={dataItemListOptions}
|
||||
/>
|
||||
</FormItem>
|
||||
)}
|
||||
{chatConfigType === ChatConfigType.AGG && (
|
||||
<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}
|
||||
/>
|
||||
</FormItem>
|
||||
)}
|
||||
|
||||
<FormItem
|
||||
label={
|
||||
<FormItemTitle
|
||||
title={'时间范围'}
|
||||
subTitle={'问答搜索结果选择中,如果没有指定时间范围,将会采用默认时间范围'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Input.Group compact>
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
lineHeight: '32px',
|
||||
marginRight: '8px',
|
||||
}}
|
||||
>
|
||||
{chatConfigType === ChatConfigType.DETAIL ? '前' : '最近'}
|
||||
</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(DefaultSettingForm);
|
||||
@@ -0,0 +1,205 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Button, Modal, message, Tabs } from 'antd';
|
||||
|
||||
import { addDomainExtend, editDomainExtend } from '../../service';
|
||||
import DimensionMetricVisibleTransfer from './DimensionMetricVisibleTransfer';
|
||||
import { IChatConfig } from '../../data';
|
||||
import DimensionValueSettingForm from './DimensionValueSettingForm';
|
||||
import { TransType } from '../../enum';
|
||||
import { wrapperTransTypeAndId, formatRichEntityDataListToIds } from './utils';
|
||||
|
||||
type Props = {
|
||||
domainId: number;
|
||||
entityData: any;
|
||||
chatConfigKey: string;
|
||||
settingSourceList: any[];
|
||||
onCancel: () => void;
|
||||
visible: boolean;
|
||||
onSubmit: (params?: any) => void;
|
||||
};
|
||||
|
||||
const dimensionConfig = {
|
||||
blackIdListKey: 'blackDimIdList',
|
||||
visibleIdListKey: 'whiteDimIdList',
|
||||
modalTitle: '问答可见信息',
|
||||
titles: ['不可见维度/指标', '可见维度/指标'],
|
||||
};
|
||||
|
||||
const DimensionAndMetricVisibleModal: React.FC<Props> = ({
|
||||
domainId,
|
||||
visible,
|
||||
entityData = {},
|
||||
chatConfigKey,
|
||||
settingSourceList,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
}) => {
|
||||
const [selectedKeyList, setSelectedKeyList] = useState<string[]>([]);
|
||||
const settingTypeConfig = dimensionConfig;
|
||||
const formatEntityData = formatRichEntityDataListToIds(entityData);
|
||||
const [knowledgeInfosMap, setKnowledgeInfosMap] = useState<IChatConfig.IKnowledgeInfosItemMap>(
|
||||
{},
|
||||
);
|
||||
const formRef = useRef<any>();
|
||||
|
||||
const [globalKnowledgeConfigInitialValues, setGlobalKnowledgeConfigInitialValues] =
|
||||
useState<IChatConfig.IKnowledgeConfig>();
|
||||
|
||||
useEffect(() => {
|
||||
if (entityData?.visibility && Array.isArray(settingSourceList)) {
|
||||
const { whiteDimIdList, whiteMetricIdList } = entityData.visibility;
|
||||
const dimensionIdString = whiteDimIdList.map((dimensionId: number) => {
|
||||
return wrapperTransTypeAndId(TransType.DIMENSION, dimensionId);
|
||||
});
|
||||
const metricIdString = whiteMetricIdList.map((metricId: number) => {
|
||||
return wrapperTransTypeAndId(TransType.METRIC, metricId);
|
||||
});
|
||||
setSelectedKeyList([...dimensionIdString, ...metricIdString]);
|
||||
}
|
||||
if (entityData?.globalKnowledgeConfig) {
|
||||
setGlobalKnowledgeConfigInitialValues(entityData.globalKnowledgeConfig);
|
||||
}
|
||||
if (Array.isArray(entityData?.knowledgeInfos)) {
|
||||
const infoMap = entityData.knowledgeInfos.reduce(
|
||||
(maps: IChatConfig.IKnowledgeInfosItemMap, item: IChatConfig.IKnowledgeInfosItem) => {
|
||||
const { bizName } = item;
|
||||
maps[bizName] = item;
|
||||
return maps;
|
||||
},
|
||||
{},
|
||||
);
|
||||
setKnowledgeInfosMap(infoMap);
|
||||
}
|
||||
}, [entityData, settingSourceList]);
|
||||
|
||||
const saveEntity = async () => {
|
||||
const globalKnowledgeConfigFormFields = await formRef?.current?.getFormValidateFields?.();
|
||||
let globalKnowledgeConfig = entityData.globalKnowledgeConfig;
|
||||
if (globalKnowledgeConfigFormFields) {
|
||||
globalKnowledgeConfig = globalKnowledgeConfigFormFields;
|
||||
}
|
||||
const { id } = entityData;
|
||||
let saveDomainExtendQuery = addDomainExtend;
|
||||
if (id) {
|
||||
saveDomainExtendQuery = editDomainExtend;
|
||||
}
|
||||
|
||||
const blackIdListMap = settingSourceList.reduce(
|
||||
(ids, item) => {
|
||||
const { id, transType } = item;
|
||||
if (!selectedKeyList.includes(wrapperTransTypeAndId(transType, id))) {
|
||||
if (transType === TransType.DIMENSION) {
|
||||
ids.blackDimIdList.push(id);
|
||||
}
|
||||
if (transType === TransType.METRIC) {
|
||||
ids.blackMetricIdList.push(id);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
},
|
||||
{
|
||||
blackDimIdList: [],
|
||||
blackMetricIdList: [],
|
||||
},
|
||||
);
|
||||
|
||||
const knowledgeInfos = Object.keys(knowledgeInfosMap).reduce(
|
||||
(infoList: IChatConfig.IKnowledgeInfosItem[], key: string) => {
|
||||
const target = knowledgeInfosMap[key];
|
||||
if (target.searchEnable) {
|
||||
infoList.push(target);
|
||||
}
|
||||
return infoList;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const params = {
|
||||
...formatEntityData,
|
||||
visibility: blackIdListMap,
|
||||
knowledgeInfos,
|
||||
...(globalKnowledgeConfig ? { globalKnowledgeConfig } : {}),
|
||||
};
|
||||
|
||||
const { code, msg } = await saveDomainExtendQuery({
|
||||
[chatConfigKey]: params,
|
||||
domainId,
|
||||
id,
|
||||
});
|
||||
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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const tabItem = [
|
||||
{
|
||||
label: '可见设置',
|
||||
key: 'visibleSetting',
|
||||
children: (
|
||||
<DimensionMetricVisibleTransfer
|
||||
onKnowledgeInfosMapChange={(knowledgeInfosMap) => {
|
||||
setKnowledgeInfosMap(knowledgeInfosMap);
|
||||
}}
|
||||
knowledgeInfosMap={knowledgeInfosMap}
|
||||
titles={settingTypeConfig.titles}
|
||||
sourceList={settingSourceList}
|
||||
targetList={selectedKeyList}
|
||||
onChange={(newTargetKeys) => {
|
||||
handleTransferChange(newTargetKeys);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: '全局维度值过滤',
|
||||
key: 'dimensionValueFilter',
|
||||
children: (
|
||||
<DimensionValueSettingForm
|
||||
initialValues={globalKnowledgeConfigInitialValues}
|
||||
ref={formRef}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
width={1200}
|
||||
destroyOnClose
|
||||
title={settingTypeConfig.modalTitle}
|
||||
maskClosable={false}
|
||||
open={visible}
|
||||
footer={renderFooter()}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<Tabs items={tabItem} defaultActiveKey="visibleSetting" />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DimensionAndMetricVisibleModal;
|
||||
@@ -3,11 +3,13 @@ import type { ForwardRefRenderFunction } from 'react';
|
||||
import { Form, Button } from 'antd';
|
||||
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
|
||||
import { formLayout } from '@/components/FormHelper/utils';
|
||||
import DimensionMetricVisibleModal from './DimensionMetricVisibleModal';
|
||||
import DimensionSearchVisibleModal from './DimensionSearchVisibleModal';
|
||||
import DimensionAndMetricVisibleModal from './DimensionAndMetricVisibleModal';
|
||||
import { TransType } from '../../enum';
|
||||
import { wrapperTransTypeAndId } from './utils';
|
||||
|
||||
type Props = {
|
||||
themeData: any;
|
||||
entityData: any;
|
||||
chatConfigKey: string;
|
||||
metricList: any[];
|
||||
dimensionList: any[];
|
||||
domainId: number;
|
||||
@@ -20,18 +22,17 @@ const DimensionMetricVisibleForm: ForwardRefRenderFunction<any, Props> = ({
|
||||
domainId,
|
||||
metricList,
|
||||
dimensionList,
|
||||
themeData,
|
||||
entityData,
|
||||
chatConfigKey,
|
||||
onSubmit,
|
||||
}) => {
|
||||
const [dimensionModalVisible, setDimensionModalVisible] = useState(false);
|
||||
const [dimensionSearchModalVisible, setDimensionSearchModalVisible] = useState(false);
|
||||
const [metricModalVisible, setMetricModalVisible] = useState<boolean>(false);
|
||||
return (
|
||||
<>
|
||||
<Form {...formLayout}>
|
||||
<FormItem
|
||||
label={
|
||||
<FormItemTitle title={'可见维度'} subTitle={'设置可见后,维度将允许在问答中被使用'} />
|
||||
<FormItemTitle title={'可见维度/指标'} subTitle={'设置可见后,将允许在问答中被使用'} />
|
||||
}
|
||||
>
|
||||
<Button
|
||||
@@ -43,44 +44,32 @@ const DimensionMetricVisibleForm: ForwardRefRenderFunction<any, Props> = ({
|
||||
设 置
|
||||
</Button>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label={
|
||||
<FormItemTitle title={'可见指标'} subTitle={'设置可见后,指标将允许在问答中被使用'} />
|
||||
}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setMetricModalVisible(true);
|
||||
}}
|
||||
>
|
||||
设 置
|
||||
</Button>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label={
|
||||
<FormItemTitle
|
||||
title={'可见维度值'}
|
||||
subTitle={'设置可见后,在可见维度设置的基础上,维度值将在搜索时可以被联想出来'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setDimensionSearchModalVisible(true);
|
||||
}}
|
||||
>
|
||||
设 置
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
{dimensionModalVisible && (
|
||||
<DimensionMetricVisibleModal
|
||||
<DimensionAndMetricVisibleModal
|
||||
domainId={domainId}
|
||||
themeData={themeData}
|
||||
settingSourceList={dimensionList}
|
||||
settingType="dimension"
|
||||
entityData={entityData}
|
||||
chatConfigKey={chatConfigKey}
|
||||
settingSourceList={[
|
||||
...dimensionList.map((item) => {
|
||||
const transType = TransType.DIMENSION;
|
||||
const { id } = item;
|
||||
return {
|
||||
...item,
|
||||
transType,
|
||||
key: wrapperTransTypeAndId(transType, id),
|
||||
};
|
||||
}),
|
||||
...metricList.map((item) => {
|
||||
const transType = TransType.METRIC;
|
||||
const { id } = item;
|
||||
return {
|
||||
...item,
|
||||
transType,
|
||||
key: wrapperTransTypeAndId(transType, id),
|
||||
};
|
||||
}),
|
||||
]}
|
||||
visible={dimensionModalVisible}
|
||||
onCancel={() => {
|
||||
setDimensionModalVisible(false);
|
||||
@@ -91,43 +80,6 @@ const DimensionMetricVisibleForm: ForwardRefRenderFunction<any, Props> = ({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{dimensionSearchModalVisible && (
|
||||
<DimensionSearchVisibleModal
|
||||
domainId={domainId}
|
||||
settingSourceList={dimensionList.filter((item) => {
|
||||
const blackDimensionList = themeData.visibility?.blackDimIdList;
|
||||
if (Array.isArray(blackDimensionList)) {
|
||||
return !blackDimensionList.includes(item.id);
|
||||
}
|
||||
return false;
|
||||
})}
|
||||
themeData={themeData}
|
||||
visible={dimensionSearchModalVisible}
|
||||
onCancel={() => {
|
||||
setDimensionSearchModalVisible(false);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
onSubmit?.({ from: 'dimensionSearchVisible' });
|
||||
setDimensionSearchModalVisible(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{metricModalVisible && (
|
||||
<DimensionMetricVisibleModal
|
||||
domainId={domainId}
|
||||
themeData={themeData}
|
||||
settingSourceList={metricList}
|
||||
settingType="metric"
|
||||
visible={metricModalVisible}
|
||||
onCancel={() => {
|
||||
setMetricModalVisible(false);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
onSubmit?.();
|
||||
setMetricModalVisible(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
import { Space, Table, Transfer, Checkbox, Tooltip, 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';
|
||||
|
||||
interface RecordType {
|
||||
id: number;
|
||||
key: string;
|
||||
name: string;
|
||||
bizName: string;
|
||||
type: TransType.DIMENSION | TransType.METRIC;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
knowledgeInfosMap: IChatConfig.IKnowledgeInfosItemMap;
|
||||
onKnowledgeInfosMapChange: (knowledgeInfosMap: IChatConfig.IKnowledgeInfosItemMap) => void;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
const DimensionMetricVisibleTableTransfer: React.FC<Props> = ({
|
||||
knowledgeInfosMap,
|
||||
onKnowledgeInfosMapChange,
|
||||
...restProps
|
||||
}) => {
|
||||
const [dimensionValueSettingModalVisible, setDimensionValueSettingModalVisible] =
|
||||
useState<boolean>(false);
|
||||
const [currentRecord, setCurrentRecord] = useState<any>({});
|
||||
const [currentDimensionSettingFormData, setCurrentDimensionSettingFormData] =
|
||||
useState<IChatConfig.IKnowledgeConfig>();
|
||||
|
||||
const updateKnowledgeInfosMap = (record: RecordType, updateData: Record<string, any>) => {
|
||||
const { bizName, id } = record;
|
||||
const knowledgeMap = {
|
||||
...knowledgeInfosMap,
|
||||
};
|
||||
const target = knowledgeMap[bizName];
|
||||
if (target) {
|
||||
knowledgeMap[bizName] = {
|
||||
...target,
|
||||
...updateData,
|
||||
};
|
||||
} else {
|
||||
knowledgeMap[bizName] = {
|
||||
itemId: id,
|
||||
bizName,
|
||||
...updateData,
|
||||
};
|
||||
}
|
||||
onKnowledgeInfosMapChange?.(knowledgeMap);
|
||||
};
|
||||
|
||||
const rightColumns: ColumnsType<RecordType> = [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: '名称',
|
||||
},
|
||||
{
|
||||
dataIndex: 'type',
|
||||
width: 80,
|
||||
title: '类型',
|
||||
render: (type) => {
|
||||
return <TransTypeTag type={type} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'y',
|
||||
title: (
|
||||
<Space>
|
||||
<span>维度值可见</span>
|
||||
<Tooltip title="勾选可见后,维度值将在搜索时可以被联想出来">
|
||||
<ExclamationCircleOutlined />
|
||||
</Tooltip>
|
||||
</Space>
|
||||
),
|
||||
width: 120,
|
||||
render: (_, record) => {
|
||||
const { type, bizName } = record;
|
||||
return type === TransType.DIMENSION ? (
|
||||
<Checkbox
|
||||
checked={knowledgeInfosMap[bizName]?.searchEnable}
|
||||
onChange={(e: CheckboxChangeEvent) => {
|
||||
updateKnowledgeInfosMap(record, { searchEnable: e.target.checked });
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'x',
|
||||
render: (_, record) => {
|
||||
const { type, bizName } = record;
|
||||
return type === TransType.DIMENSION ? (
|
||||
<Button
|
||||
style={{ padding: 0 }}
|
||||
key="editable"
|
||||
type="link"
|
||||
disabled={!knowledgeInfosMap[bizName]?.searchEnable}
|
||||
onClick={(event) => {
|
||||
setCurrentRecord(record);
|
||||
setCurrentDimensionSettingFormData(
|
||||
knowledgeInfosMap[bizName]?.knowledgeAdvancedConfig,
|
||||
);
|
||||
setDimensionValueSettingModalVisible(true);
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
可见维度值设置
|
||||
</Button>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const leftColumns: ColumnsType<RecordType> = [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: '名称',
|
||||
},
|
||||
{
|
||||
dataIndex: 'type',
|
||||
title: '类型',
|
||||
render: (type) => {
|
||||
return <TransTypeTag type={type} />;
|
||||
},
|
||||
},
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<Transfer {...restProps}>
|
||||
{({
|
||||
direction,
|
||||
filteredItems,
|
||||
onItemSelectAll,
|
||||
onItemSelect,
|
||||
selectedKeys: listSelectedKeys,
|
||||
}) => {
|
||||
const columns = direction === 'left' ? leftColumns : rightColumns;
|
||||
const rowSelection: TableRowSelection<TransferItem> = {
|
||||
onSelectAll(selected, selectedRows) {
|
||||
const treeSelectedKeys = selectedRows.map(({ key }) => key);
|
||||
const diffKeys = selected
|
||||
? difference(treeSelectedKeys, listSelectedKeys)
|
||||
: difference(listSelectedKeys, treeSelectedKeys);
|
||||
onItemSelectAll(diffKeys as string[], selected);
|
||||
},
|
||||
onSelect({ key }, selected) {
|
||||
onItemSelect(key as string, selected);
|
||||
},
|
||||
selectedRowKeys: listSelectedKeys,
|
||||
};
|
||||
|
||||
return (
|
||||
<Table
|
||||
rowSelection={rowSelection}
|
||||
columns={columns}
|
||||
dataSource={filteredItems as any}
|
||||
size="small"
|
||||
pagination={false}
|
||||
scroll={{ y: 450 }}
|
||||
onRow={({ key }) => ({
|
||||
onClick: () => {
|
||||
onItemSelect(key as string, !listSelectedKeys.includes(key as string));
|
||||
},
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Transfer>
|
||||
<DimensionValueSettingModal
|
||||
visible={dimensionValueSettingModalVisible}
|
||||
initialValues={currentDimensionSettingFormData}
|
||||
onSubmit={(formValues) => {
|
||||
updateKnowledgeInfosMap(currentRecord, { knowledgeAdvancedConfig: formValues });
|
||||
setDimensionValueSettingModalVisible(false);
|
||||
}}
|
||||
onCancel={() => {
|
||||
setDimensionValueSettingModalVisible(false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DimensionMetricVisibleTableTransfer;
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Transfer, Tag } from 'antd';
|
||||
import { Tag } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { IChatConfig } from '../../data';
|
||||
import DimensionMetricVisibleTableTransfer from './DimensionMetricVisibleTableTransfer';
|
||||
|
||||
interface RecordType {
|
||||
key: string;
|
||||
@@ -8,14 +10,18 @@ interface RecordType {
|
||||
}
|
||||
|
||||
type Props = {
|
||||
knowledgeInfosMap: IChatConfig.IKnowledgeInfosItemMap;
|
||||
sourceList: any[];
|
||||
targetList: string[];
|
||||
titles?: string[];
|
||||
onKnowledgeInfosMapChange: (knowledgeInfosMap: IChatConfig.IKnowledgeInfosItemMap) => void;
|
||||
onChange?: (params?: any) => void;
|
||||
transferProps?: Record<string, any>;
|
||||
};
|
||||
|
||||
const DimensionMetricVisibleTransfer: React.FC<Props> = ({
|
||||
knowledgeInfosMap,
|
||||
onKnowledgeInfosMapChange,
|
||||
sourceList = [],
|
||||
targetList = [],
|
||||
titles,
|
||||
@@ -27,11 +33,13 @@ const DimensionMetricVisibleTransfer: React.FC<Props> = ({
|
||||
|
||||
useEffect(() => {
|
||||
setTransferData(
|
||||
sourceList.map(({ id, name, type }: any) => {
|
||||
sourceList.map(({ key, id, name, bizName, transType }) => {
|
||||
return {
|
||||
key: id,
|
||||
key,
|
||||
name,
|
||||
type,
|
||||
bizName,
|
||||
id,
|
||||
type: transType,
|
||||
};
|
||||
}),
|
||||
);
|
||||
@@ -48,13 +56,15 @@ const DimensionMetricVisibleTransfer: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Transfer
|
||||
<DimensionMetricVisibleTableTransfer
|
||||
knowledgeInfosMap={knowledgeInfosMap}
|
||||
onKnowledgeInfosMapChange={onKnowledgeInfosMapChange}
|
||||
dataSource={transferData}
|
||||
showSearch
|
||||
titles={titles || ['不可见维度', '可见维度']}
|
||||
listStyle={{
|
||||
width: 430,
|
||||
height: 500,
|
||||
width: 500,
|
||||
height: 600,
|
||||
}}
|
||||
filterOption={(inputValue: string, item: any) => {
|
||||
const { name } = item;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { useEffect, forwardRef, useImperativeHandle } from 'react';
|
||||
import type { ForwardRefRenderFunction } from 'react';
|
||||
import { Form, Input } from 'antd';
|
||||
|
||||
import { formLayout } from '@/components/FormHelper/utils';
|
||||
import { isString } from 'lodash';
|
||||
import styles from '../style.less';
|
||||
|
||||
import SqlEditor from '@/components/SqlEditor';
|
||||
type Props = {
|
||||
initialValues: any;
|
||||
onSubmit?: () => void;
|
||||
};
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
const EntityCreateForm: ForwardRefRenderFunction<any, Props> = ({ initialValues }, ref) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const exchangeFields = ['blackList', 'whiteList', 'ruleList'];
|
||||
|
||||
const getFormValidateFields = async () => {
|
||||
const fields = await form.validateFields();
|
||||
const fieldValue = Object.keys(fields).reduce((formField, key: string) => {
|
||||
const targetValue = fields[key];
|
||||
if (isString(targetValue) && exchangeFields.includes(key)) {
|
||||
formField[key] = targetValue.split(',');
|
||||
} else {
|
||||
formField[key] = targetValue;
|
||||
}
|
||||
return formField;
|
||||
}, {});
|
||||
return {
|
||||
...fieldValue,
|
||||
};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
if (!initialValues) {
|
||||
return;
|
||||
}
|
||||
const fieldValue = Object.keys(initialValues).reduce((formField, key: string) => {
|
||||
const targetValue = initialValues[key];
|
||||
if (Array.isArray(targetValue) && exchangeFields.includes(key)) {
|
||||
formField[key] = targetValue.join(',');
|
||||
} else {
|
||||
formField[key] = targetValue;
|
||||
}
|
||||
return formField;
|
||||
}, {});
|
||||
form.setFieldsValue({
|
||||
...fieldValue,
|
||||
});
|
||||
}, [initialValues]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getFormValidateFields,
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...formLayout} form={form} layout="vertical" className={styles.form}>
|
||||
<FormItem name="blackList" label="黑名单">
|
||||
<Input placeholder="多个维度值用英文逗号隔开" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem name="whiteList" label="白名单">
|
||||
<Input placeholder="多个维度值用英文逗号隔开" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem name="ruleList" label="过滤规则">
|
||||
<SqlEditor height={'150px'} />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default forwardRef(EntityCreateForm);
|
||||
@@ -0,0 +1,56 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { Button, Modal } from 'antd';
|
||||
import DimensionValueSettingForm from './DimensionValueSettingForm';
|
||||
|
||||
type Props = {
|
||||
initialValues: any;
|
||||
onCancel?: () => void;
|
||||
visible: boolean;
|
||||
onSubmit?: (params?: any) => void;
|
||||
};
|
||||
const DimensionValueSettingModal: React.FC<Props> = ({
|
||||
initialValues,
|
||||
visible,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
}) => {
|
||||
const formRef = useRef<any>();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const formValues = await formRef.current.getFormValidateFields();
|
||||
onSubmit?.(formValues);
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
return (
|
||||
<>
|
||||
<Button onClick={onCancel}>取消</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
handleSubmit();
|
||||
}}
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
width={600}
|
||||
destroyOnClose
|
||||
title={'维度值设置'}
|
||||
maskClosable={false}
|
||||
open={visible}
|
||||
footer={renderFooter()}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<DimensionValueSettingForm initialValues={initialValues} ref={formRef} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DimensionValueSettingModal;
|
||||
@@ -4,27 +4,24 @@ import { message, Form, Input, Select, Button } from 'antd';
|
||||
import { addDomainExtend, editDomainExtend } from '../../service';
|
||||
import type { ISemantic, IChatConfig } from '../../data';
|
||||
import { formLayout } from '@/components/FormHelper/utils';
|
||||
import { exChangeRichEntityListToIds } from './utils';
|
||||
import { formatRichEntityDataListToIds } from './utils';
|
||||
import styles from '../style.less';
|
||||
|
||||
type Props = {
|
||||
entityData: IChatConfig.IEntity;
|
||||
metricList: ISemantic.IMetricList;
|
||||
entityData: IChatConfig.IChatRichConfig;
|
||||
dimensionList: ISemantic.IDimensionList;
|
||||
domainId: number;
|
||||
onSubmit: () => void;
|
||||
};
|
||||
|
||||
const FormItem = Form.Item;
|
||||
const TextArea = Input.TextArea;
|
||||
|
||||
const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
{ entityData, metricList, dimensionList, domainId, onSubmit },
|
||||
{ entityData, dimensionList, domainId, onSubmit },
|
||||
ref,
|
||||
) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [metricListOptions, setMetricListOptions] = useState<any>([]);
|
||||
const formatEntityData = formatRichEntityDataListToIds(entityData);
|
||||
const [dimensionListOptions, setDimensionListOptions] = useState<any>([]);
|
||||
|
||||
const getFormValidateFields = async () => {
|
||||
@@ -33,29 +30,19 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
if (Object.keys(entityData).length === 0) {
|
||||
if (!entityData?.entity) {
|
||||
return;
|
||||
}
|
||||
const names = entityData.names || [];
|
||||
const formatEntityData = exChangeRichEntityListToIds(entityData);
|
||||
|
||||
form.setFieldsValue({
|
||||
...formatEntityData,
|
||||
name: names.join(','),
|
||||
...formatEntityData.entity,
|
||||
id: formatEntityData.id,
|
||||
});
|
||||
}, [entityData]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getFormValidateFields,
|
||||
}));
|
||||
useEffect(() => {
|
||||
const metricOption = metricList.map((item: ISemantic.IMetricItem) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
});
|
||||
setMetricListOptions(metricOption);
|
||||
}, [metricList]);
|
||||
|
||||
useEffect(() => {
|
||||
const dimensionEnum = dimensionList.map((item: ISemantic.IDimensionItem) => {
|
||||
@@ -75,10 +62,14 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
saveDomainExtendQuery = editDomainExtend;
|
||||
}
|
||||
const { code, msg, data } = await saveDomainExtendQuery({
|
||||
entity: {
|
||||
...values,
|
||||
names: name.split(','),
|
||||
chatDetailConfig: {
|
||||
...formatEntityData,
|
||||
entity: {
|
||||
...values,
|
||||
names: name.split(','),
|
||||
},
|
||||
},
|
||||
id,
|
||||
domainId,
|
||||
});
|
||||
|
||||
@@ -99,66 +90,31 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
</FormItem>
|
||||
<FormItem
|
||||
name="name"
|
||||
label="实体名称"
|
||||
rules={[{ required: true, message: '请输入实体名称' }]}
|
||||
label="实体别名"
|
||||
// rules={[{ required: true, message: '请输入实体别名' }]}
|
||||
>
|
||||
<TextArea
|
||||
placeholder="请输入实体名称,多个实体名称以英文逗号分隔"
|
||||
style={{ height: 100 }}
|
||||
/>
|
||||
<Input placeholder="请输入实体别名,多个名称以英文逗号分隔" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
name="entityIds"
|
||||
name="entityId"
|
||||
label="唯一标识"
|
||||
rules={[{ required: true, message: '请选择实体标识' }]}
|
||||
// rules={[{ required: true, message: '请选择实体标识' }]}
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
// mode="multiple"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
filterOption={(inputValue: string, item: any) => {
|
||||
const { label } = item;
|
||||
if (label.includes(inputValue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
// filterOption={(inputValue: string, item: any) => {
|
||||
// const { label } = item;
|
||||
// if (label.includes(inputValue)) {
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
// }}
|
||||
placeholder="请选择主体标识"
|
||||
options={dimensionListOptions}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem name={['detailData', 'dimensionIds']} label="维度信息">
|
||||
<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={dimensionListOptions}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem name={['detailData', 'metricIds']} label="指标信息">
|
||||
<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>
|
||||
<Button
|
||||
type="primary"
|
||||
|
||||
@@ -3,24 +3,28 @@ import React, { useState, useEffect, useRef } from 'react';
|
||||
import type { Dispatch } from 'umi';
|
||||
import { connect } from 'umi';
|
||||
import type { StateType } from '../../model';
|
||||
import { getDomainExtendConfig, getDomainExtendDetailConfig } from '../../service';
|
||||
import { getDomainExtendDetailConfig } from '../../service';
|
||||
import ProCard from '@ant-design/pro-card';
|
||||
import EntityCreateForm from './EntityCreateForm';
|
||||
import MetricSettingForm from './MetricSettingForm';
|
||||
import DefaultSettingForm from './DefaultSettingForm';
|
||||
import type { IChatConfig } from '../../data';
|
||||
import DimensionMetricVisibleForm from './DimensionMetricVisibleForm';
|
||||
import { ChatConfigType } from '../../enum';
|
||||
|
||||
type Props = {
|
||||
chatConfigType: ChatConfigType.DETAIL | ChatConfigType.AGG;
|
||||
dispatch: Dispatch;
|
||||
domainManger: StateType;
|
||||
};
|
||||
|
||||
const EntitySection: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
const EntitySection: React.FC<Props> = ({
|
||||
domainManger,
|
||||
dispatch,
|
||||
chatConfigType = ChatConfigType.DETAIL,
|
||||
}) => {
|
||||
const { selectDomainId, dimensionList, metricList } = domainManger;
|
||||
|
||||
const [entityData, setEntityData] = useState<IChatConfig.IEntity>({} as IChatConfig.IEntity);
|
||||
|
||||
const [themeData, setThemeData] = useState<any>({});
|
||||
const [entityData, setentityData] = useState<IChatConfig.IChatRichConfig>();
|
||||
|
||||
const entityCreateRef = useRef<any>({});
|
||||
|
||||
@@ -28,21 +32,19 @@ const EntitySection: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
const { code, data } = await getDomainExtendDetailConfig({
|
||||
domainId: selectDomainId,
|
||||
});
|
||||
// getDomainExtendConfig({
|
||||
// domainId: selectDomainId,
|
||||
// });
|
||||
|
||||
if (code === 200) {
|
||||
const target = data;
|
||||
if (target) {
|
||||
setThemeData(target);
|
||||
setEntityData({
|
||||
id: target.id,
|
||||
...target.entity,
|
||||
});
|
||||
const { chatAggRichConfig, chatDetailRichConfig, id, domainId } = data;
|
||||
if (chatConfigType === ChatConfigType.DETAIL) {
|
||||
setentityData({ ...chatDetailRichConfig, id, domainId });
|
||||
}
|
||||
if (chatConfigType === ChatConfigType.AGG) {
|
||||
setentityData({ ...chatAggRichConfig, id, domainId });
|
||||
}
|
||||
return;
|
||||
}
|
||||
message.error('获取主题域解析词失败');
|
||||
|
||||
message.error('获取问答设置信息失败');
|
||||
};
|
||||
|
||||
const initPage = async () => {
|
||||
@@ -56,9 +58,31 @@ const EntitySection: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
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
|
||||
themeData={themeData}
|
||||
chatConfigKey={
|
||||
chatConfigType === ChatConfigType.DETAIL ? 'chatDetailConfig' : 'chatAggConfig'
|
||||
}
|
||||
entityData={entityData || {}}
|
||||
domainId={Number(selectDomainId)}
|
||||
metricList={metricList}
|
||||
dimensionList={dimensionList}
|
||||
@@ -75,44 +99,28 @@ const EntitySection: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
}}
|
||||
/>
|
||||
</ProCard>
|
||||
<ProCard bordered title="默认指标">
|
||||
<MetricSettingForm
|
||||
<ProCard bordered title="默认设置">
|
||||
<DefaultSettingForm
|
||||
domainId={Number(selectDomainId)}
|
||||
themeData={themeData}
|
||||
// metricList={metricList}
|
||||
metricList={metricList.filter((item) => {
|
||||
const blackMetricIdList = themeData.visibility?.blackMetricIdList;
|
||||
if (Array.isArray(blackMetricIdList)) {
|
||||
return !blackMetricIdList.includes(item.id);
|
||||
}
|
||||
return false;
|
||||
})}
|
||||
onSubmit={() => {
|
||||
queryThemeListData();
|
||||
}}
|
||||
/>
|
||||
</ProCard>
|
||||
<ProCard title="实体" bordered>
|
||||
<EntityCreateForm
|
||||
ref={entityCreateRef}
|
||||
domainId={Number(selectDomainId)}
|
||||
entityData={entityData}
|
||||
// metricList={metricList}
|
||||
metricList={metricList.filter((item) => {
|
||||
const blackMetricIdList = themeData.visibility?.blackMetricIdList;
|
||||
if (Array.isArray(blackMetricIdList)) {
|
||||
return !blackMetricIdList.includes(item.id);
|
||||
}
|
||||
return false;
|
||||
})}
|
||||
// dimensionList={dimensionList}
|
||||
entityData={entityData || {}}
|
||||
chatConfigType={chatConfigType}
|
||||
chatConfigKey={
|
||||
chatConfigType === ChatConfigType.DETAIL ? 'chatDetailConfig' : 'chatAggConfig'
|
||||
}
|
||||
dimensionList={dimensionList.filter((item) => {
|
||||
const blackDimensionList = themeData.visibility?.blackDimIdList;
|
||||
const blackDimensionList = entityData?.visibility?.blackDimIdList;
|
||||
if (Array.isArray(blackDimensionList)) {
|
||||
return !blackDimensionList.includes(item.id);
|
||||
}
|
||||
return false;
|
||||
})}
|
||||
metricList={metricList.filter((item) => {
|
||||
const blackMetricIdList = entityData?.visibility?.blackMetricIdList;
|
||||
if (Array.isArray(blackMetricIdList)) {
|
||||
return !blackMetricIdList.includes(item.id);
|
||||
}
|
||||
return false;
|
||||
})}
|
||||
onSubmit={() => {
|
||||
queryThemeListData();
|
||||
}}
|
||||
|
||||
@@ -1,28 +1,87 @@
|
||||
import { IChatConfig, ISemantic } from '../../data';
|
||||
import { TransType } from '../../enum';
|
||||
|
||||
type FormatResult = IChatConfig.IChatRichConfig & {
|
||||
chatDefaultConfig: {
|
||||
dimensionIds: number[];
|
||||
metricIds: number[];
|
||||
};
|
||||
};
|
||||
export const formatRichEntityDataListToIds = (
|
||||
entityData: IChatConfig.IChatRichConfig,
|
||||
): FormatResult => {
|
||||
if (!entityData?.chatDefaultConfig) {
|
||||
return {} as FormatResult;
|
||||
}
|
||||
const { chatDefaultConfig, entity } = entityData;
|
||||
|
||||
export const exChangeRichEntityListToIds = (entityData: IChatConfig.IEntity) => {
|
||||
const entityList = entityData.entityIds || [];
|
||||
const detailData: {
|
||||
dimensionIds: number[];
|
||||
metricIds: number[];
|
||||
} = { dimensionIds: [], metricIds: [] };
|
||||
const { dimensionList, metricList } = entityData.entityInternalDetailDesc || {};
|
||||
if (Array.isArray(dimensionList)) {
|
||||
detailData.dimensionIds = dimensionList.map((item: ISemantic.IDimensionItem) => {
|
||||
const { dimensions, metrics } = chatDefaultConfig || {};
|
||||
if (Array.isArray(dimensions)) {
|
||||
detailData.dimensionIds = dimensions.map((item: ISemantic.IDimensionItem) => {
|
||||
return item.id;
|
||||
});
|
||||
}
|
||||
if (Array.isArray(metricList)) {
|
||||
detailData.metricIds = metricList.map((item: ISemantic.IMetricItem) => {
|
||||
if (Array.isArray(metrics)) {
|
||||
detailData.metricIds = metrics.map((item: ISemantic.IMetricItem) => {
|
||||
return item.id;
|
||||
});
|
||||
}
|
||||
const entityIds = entityList.map((item) => {
|
||||
return item.id;
|
||||
});
|
||||
let entitySetting = {};
|
||||
if (entity) {
|
||||
const entityItem = entity.dimItem;
|
||||
const entityId = entityItem?.id || null;
|
||||
const names = entity.names || [];
|
||||
entitySetting = {
|
||||
entity: {
|
||||
...entity,
|
||||
entityId,
|
||||
name: names.join(','),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...entityData,
|
||||
entityIds,
|
||||
detailData,
|
||||
...entitySetting,
|
||||
chatDefaultConfig: {
|
||||
...chatDefaultConfig,
|
||||
...detailData,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const wrapperTransTypeAndId = (exTransType: TransType, id: number) => {
|
||||
return `${exTransType}-${id}`;
|
||||
};
|
||||
|
||||
export const splitListToTransTypeId = (dataItemIds: string[]) => {
|
||||
const idListMap = dataItemIds.reduce(
|
||||
(
|
||||
idMap: {
|
||||
dimensionIds: number[];
|
||||
metricIds: number[];
|
||||
},
|
||||
item: string,
|
||||
) => {
|
||||
const [transType, id] = item.split('-');
|
||||
if (id) {
|
||||
if (transType === TransType.DIMENSION) {
|
||||
idMap.dimensionIds.push(Number(id));
|
||||
}
|
||||
if (transType === TransType.METRIC) {
|
||||
idMap.metricIds.push(Number(id));
|
||||
}
|
||||
}
|
||||
return idMap;
|
||||
},
|
||||
{
|
||||
dimensionIds: [],
|
||||
metricIds: [],
|
||||
},
|
||||
);
|
||||
return idListMap;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import type { InputRef } from 'antd';
|
||||
import { Input as AntdInput, Tag, Tooltip } from 'antd';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import styles from './style.less';
|
||||
|
||||
type Props = {
|
||||
createBtnString?: string;
|
||||
value?: string[];
|
||||
onChange?: (valueList: string[]) => void;
|
||||
};
|
||||
|
||||
const InfoTagList: React.FC<Props> = ({ value, createBtnString = '新增', onChange }) => {
|
||||
const [tags, setTags] = useState<string[]>([]);
|
||||
const [inputVisible, setInputVisible] = useState(false);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [editInputIndex, setEditInputIndex] = useState(-1);
|
||||
const [editInputValue, setEditInputValue] = useState('');
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
const editInputRef = useRef<InputRef>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (Array.isArray(value)) {
|
||||
setTags([...value]);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
const handleTagChange = (tagList: string[]) => {
|
||||
console.log(tagList, 'tagList');
|
||||
onChange?.(tagList);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (inputVisible) {
|
||||
inputRef.current?.focus();
|
||||
}
|
||||
}, [inputVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
editInputRef.current?.focus();
|
||||
}, [inputValue]);
|
||||
|
||||
const handleClose = (removedTag: string) => {
|
||||
const newTags = tags.filter((tag) => tag !== removedTag);
|
||||
handleTagChange?.(newTags);
|
||||
setTags(newTags);
|
||||
};
|
||||
|
||||
const showInput = () => {
|
||||
setInputVisible(true);
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(e.target.value);
|
||||
};
|
||||
|
||||
const handleInputConfirm = () => {
|
||||
if (inputValue && tags.indexOf(inputValue) === -1) {
|
||||
const newTags = [...tags, inputValue];
|
||||
handleTagChange?.(newTags);
|
||||
setTags(newTags);
|
||||
}
|
||||
setInputVisible(false);
|
||||
setInputValue('');
|
||||
};
|
||||
|
||||
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setEditInputValue(e.target.value);
|
||||
};
|
||||
|
||||
const handleEditInputConfirm = () => {
|
||||
const newTags = [...tags];
|
||||
newTags[editInputIndex] = editInputValue;
|
||||
setTags(newTags);
|
||||
handleTagChange?.(newTags);
|
||||
setEditInputIndex(-1);
|
||||
setInputValue('');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.infoTagList}>
|
||||
{tags.map((tag, index) => {
|
||||
if (editInputIndex === index) {
|
||||
return (
|
||||
<AntdInput
|
||||
ref={editInputRef}
|
||||
key={tag}
|
||||
size="small"
|
||||
className={styles.tagInput}
|
||||
value={editInputValue}
|
||||
onChange={handleEditInputChange}
|
||||
onBlur={handleEditInputConfirm}
|
||||
onPressEnter={handleEditInputConfirm}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const isLongTag = tag.length > 20;
|
||||
|
||||
const tagElem = (
|
||||
<Tag
|
||||
className={styles.editTag}
|
||||
key={tag}
|
||||
closable={true}
|
||||
onClose={() => handleClose(tag)}
|
||||
>
|
||||
<span
|
||||
onDoubleClick={(e) => {
|
||||
setEditInputIndex(index);
|
||||
setEditInputValue(tag);
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
{isLongTag ? `${tag.slice(0, 20)}...` : tag}
|
||||
</span>
|
||||
</Tag>
|
||||
);
|
||||
return isLongTag ? (
|
||||
<Tooltip title={tag} key={tag}>
|
||||
{tagElem}
|
||||
</Tooltip>
|
||||
) : (
|
||||
tagElem
|
||||
);
|
||||
})}
|
||||
{inputVisible && (
|
||||
<AntdInput
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
size="small"
|
||||
className={styles.tagInput}
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
onBlur={handleInputConfirm}
|
||||
onPressEnter={handleInputConfirm}
|
||||
/>
|
||||
)}
|
||||
{!inputVisible && (
|
||||
<Tag className={styles.siteTagPlus} onClick={showInput}>
|
||||
<PlusOutlined /> {createBtnString}
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfoTagList;
|
||||
@@ -88,6 +88,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
typeParams: typeParams,
|
||||
dataFormat,
|
||||
dataFormatType,
|
||||
alias,
|
||||
} = metricItem as any;
|
||||
const isPercent = dataFormatType === 'percent';
|
||||
const initValue = {
|
||||
@@ -97,6 +98,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
sensitiveLevel,
|
||||
description,
|
||||
isPercent,
|
||||
alias,
|
||||
dataFormat: dataFormat || {
|
||||
decimalPlaces: 2,
|
||||
needMultiply100: false,
|
||||
|
||||
@@ -72,7 +72,7 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
||||
dataIndex: 'constraint',
|
||||
title: '限定条件',
|
||||
tooltip:
|
||||
'所用于过滤的维度需要存在于"维度"列表,不需要加where关键字。比如:维度A="值1" and 维度B="值2"',
|
||||
'该限定条件用于在计算指标时限定口径,作用于度量,所用于过滤的维度必须在创建数据源的时候被标记为日期或者维度,不需要加where关键字。比如:维度A="值1" and 维度B="值2"',
|
||||
render: (_: any, record: any) => {
|
||||
const { constraint, name } = record;
|
||||
const { measures } = measuresParams;
|
||||
@@ -128,7 +128,7 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
headerTitle="度量列表"
|
||||
tooltip="一般用于在“指标”列表已有指标的基础上加工新指标,比如:指标NEW1=指标A/100,指标NEW2=指标B/指标C。(若需用到多个已有指标,可以点击右上角“增加度量”)"
|
||||
tooltip="基于本主题域下所有数据源的度量来创建指标,且该列表的度量为了加以区分,均已加上数据源名称作为前缀,选中度量后,可基于这几个度量来写表达式,若是选中的度量来自不同的数据源,系统将会自动join来计算该指标"
|
||||
rowKey="name"
|
||||
columns={columns}
|
||||
dataSource={measuresParams?.measures || []}
|
||||
@@ -150,7 +150,7 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
|
||||
/>
|
||||
<ProCard
|
||||
title={'度量表达式'}
|
||||
tooltip="若为指标NEW1,则填写:指标A/100。若为指标NEW2,则填写:指标B/指标C"
|
||||
tooltip="度量表达式由上面选择的度量组成,如选择了度量A和B,则可将表达式写成A+B"
|
||||
>
|
||||
<SqlEditor
|
||||
value={exprString}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { CheckCard } from '@ant-design/pro-components';
|
||||
import React from 'react';
|
||||
import { ISemantic } from '../data';
|
||||
import { connect } from 'umi';
|
||||
import icon from '../../../assets/icon/cloudEditor.svg';
|
||||
import type { Dispatch } from 'umi';
|
||||
import type { StateType } from '../model';
|
||||
import { formatNumber } from '../../../utils/utils';
|
||||
import styles from './style.less';
|
||||
|
||||
type Props = {
|
||||
modelList: ISemantic.IDomainItem[];
|
||||
domainManger: StateType;
|
||||
dispatch: Dispatch;
|
||||
};
|
||||
|
||||
const OverView: React.FC<Props> = ({ domainManger, dispatch, modelList }) => {
|
||||
const { selectDomainId } = domainManger;
|
||||
|
||||
const extraNode = (model: ISemantic.IDomainItem) => {
|
||||
const { metricCnt, dimensionCnt } = model;
|
||||
return (
|
||||
<div className={styles.overviewExtraContainer}>
|
||||
<div className={styles.extraWrapper}>
|
||||
<div className={styles.extraStatistic}>
|
||||
<div className={styles.extraTitle}>维度数</div>
|
||||
<div className={styles.extraValue}>
|
||||
<span className="ant-statistic-content-value">{formatNumber(dimensionCnt || 0)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.extraWrapper}>
|
||||
<div className={styles.extraStatistic}>
|
||||
<div className={styles.extraTitle}>指标数</div>
|
||||
<div className={styles.extraValue}>
|
||||
<span className="ant-statistic-content-value">{formatNumber(metricCnt || 0)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<CheckCard.Group value={selectDomainId} defaultValue={selectDomainId}>
|
||||
{modelList &&
|
||||
modelList.map((model: ISemantic.IDomainItem) => {
|
||||
return (
|
||||
<CheckCard
|
||||
avatar={icon}
|
||||
title={model.name}
|
||||
key={model.id}
|
||||
value={model.id}
|
||||
// description={model.description || '模型描述...'}
|
||||
description={extraNode(model)}
|
||||
onClick={() => {
|
||||
const { id, name } = model;
|
||||
dispatch({
|
||||
type: 'domainManger/setSelectDomain',
|
||||
selectDomainId: id,
|
||||
selectDomainName: name,
|
||||
domainData: model,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</CheckCard.Group>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ domainManger }: { domainManger: StateType }) => ({
|
||||
domainManger,
|
||||
}))(OverView);
|
||||
@@ -6,23 +6,26 @@ import type { FC, Key } from 'react';
|
||||
import { connect } from 'umi';
|
||||
import type { Dispatch } from 'umi';
|
||||
import type { StateType } from '../model';
|
||||
import { getDomainList, createDomain, updateDomain, deleteDomain } from '../service';
|
||||
import { createDomain, updateDomain, deleteDomain } from '../service';
|
||||
import { treeParentKeyLists } from '../utils';
|
||||
import ProjectInfoFormProps from './ProjectInfoForm';
|
||||
import { constructorClassTreeFromList, addPathInTreeData } from '../utils';
|
||||
import { PlusCircleOutlined } from '@ant-design/icons';
|
||||
|
||||
import styles from './style.less';
|
||||
import { ISemantic } from '../data';
|
||||
|
||||
const { Search } = Input;
|
||||
|
||||
type ProjectListProps = {
|
||||
selectDomainId: string;
|
||||
selectDomainId: number;
|
||||
selectDomainName: string;
|
||||
domainList: ISemantic.IDomainItem[];
|
||||
createDomainBtnVisible?: boolean;
|
||||
dispatch: Dispatch;
|
||||
onCreateDomainBtnClick?: () => void;
|
||||
onTreeSelected?: () => void;
|
||||
onTreeDataUpdate?: () => void;
|
||||
};
|
||||
|
||||
const projectTreeFlat = (projectTree: DataNode[], filterValue: string): DataNode[] => {
|
||||
@@ -42,9 +45,11 @@ const projectTreeFlat = (projectTree: DataNode[], filterValue: string): DataNode
|
||||
|
||||
const ProjectListTree: FC<ProjectListProps> = ({
|
||||
selectDomainId,
|
||||
domainList,
|
||||
createDomainBtnVisible = true,
|
||||
onCreateDomainBtnClick,
|
||||
onTreeSelected,
|
||||
onTreeDataUpdate,
|
||||
dispatch,
|
||||
}) => {
|
||||
const [projectTree, setProjectTree] = useState<DataNode[]>([]);
|
||||
@@ -54,40 +59,19 @@ const ProjectListTree: FC<ProjectListProps> = ({
|
||||
const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
|
||||
const [classList, setClassList] = useState<any[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const treeData = addPathInTreeData(constructorClassTreeFromList(domainList));
|
||||
setProjectTree(treeData);
|
||||
setClassList(domainList);
|
||||
setExpandedKeys(treeParentKeyLists(treeData));
|
||||
}, [domainList]);
|
||||
|
||||
const onSearch = (value: any) => {
|
||||
setFliterValue(value);
|
||||
};
|
||||
|
||||
const initProjectTree = async () => {
|
||||
const { code, data, msg } = await getDomainList();
|
||||
if (code === 200) {
|
||||
const treeData = addPathInTreeData(constructorClassTreeFromList(data));
|
||||
setProjectTree(treeData);
|
||||
setClassList(data);
|
||||
setExpandedKeys(treeParentKeyLists(treeData));
|
||||
const firstRootNode = data.filter((item: any) => {
|
||||
return item.parentId === 0;
|
||||
})[0];
|
||||
if (firstRootNode) {
|
||||
const { id, name } = firstRootNode;
|
||||
dispatch({
|
||||
type: 'domainManger/setSelectDomain',
|
||||
selectDomainId: id,
|
||||
selectDomainName: name,
|
||||
domainData: firstRootNode,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
message.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
initProjectTree();
|
||||
}, []);
|
||||
|
||||
const handleSelect = (selectedKeys: string, projectName: string) => {
|
||||
if (selectedKeys === selectDomainId) {
|
||||
if (`${selectedKeys}` === `${selectDomainId}`) {
|
||||
return;
|
||||
}
|
||||
const targetNodeData = classList.filter((item: any) => {
|
||||
@@ -108,7 +92,7 @@ const ProjectListTree: FC<ProjectListProps> = ({
|
||||
if (res.code === 200) {
|
||||
message.success('编辑分类成功');
|
||||
setProjectInfoModalVisible(false);
|
||||
initProjectTree();
|
||||
onTreeDataUpdate?.();
|
||||
} else {
|
||||
message.error(res.msg);
|
||||
}
|
||||
@@ -120,7 +104,7 @@ const ProjectListTree: FC<ProjectListProps> = ({
|
||||
} else if (values.modelType === 'edit') {
|
||||
await editProject(values);
|
||||
}
|
||||
initProjectTree();
|
||||
onTreeDataUpdate?.();
|
||||
setProjectInfoModalVisible(false);
|
||||
};
|
||||
|
||||
@@ -130,7 +114,7 @@ const ProjectListTree: FC<ProjectListProps> = ({
|
||||
if (res.code === 200) {
|
||||
message.success('编辑项目成功');
|
||||
setProjectInfoModalVisible(false);
|
||||
initProjectTree();
|
||||
onTreeDataUpdate?.();
|
||||
} else {
|
||||
message.error(res.msg);
|
||||
}
|
||||
@@ -210,18 +194,20 @@ const ProjectListTree: FC<ProjectListProps> = ({
|
||||
onSearch={onSearch}
|
||||
/>
|
||||
</Col>
|
||||
<Col flex="0 1 50px">
|
||||
<Tooltip title="新增顶级域">
|
||||
<PlusCircleOutlined
|
||||
onClick={() => {
|
||||
setProjectInfoParams({ type: 'top', modelType: 'add' });
|
||||
setProjectInfoModalVisible(true);
|
||||
onCreateDomainBtnClick?.();
|
||||
}}
|
||||
className={styles.addBtn}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
{createDomainBtnVisible && (
|
||||
<Col flex="0 1 50px">
|
||||
<Tooltip title="新增顶级域">
|
||||
<PlusCircleOutlined
|
||||
onClick={() => {
|
||||
setProjectInfoParams({ type: 'top', modelType: 'add' });
|
||||
setProjectInfoModalVisible(true);
|
||||
onCreateDomainBtnClick?.();
|
||||
}}
|
||||
className={styles.addBtn}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
<Tree
|
||||
@@ -249,8 +235,13 @@ const ProjectListTree: FC<ProjectListProps> = ({
|
||||
};
|
||||
|
||||
export default connect(
|
||||
({ domainManger: { selectDomainId, selectDomainName } }: { domainManger: StateType }) => ({
|
||||
({
|
||||
domainManger: { selectDomainId, selectDomainName, domainList },
|
||||
}: {
|
||||
domainManger: StateType;
|
||||
}) => ({
|
||||
selectDomainId,
|
||||
selectDomainName,
|
||||
domainList,
|
||||
}),
|
||||
)(ProjectListTree);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Tag } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
import { TransType } from '../enum';
|
||||
|
||||
type Props = {
|
||||
type: TransType;
|
||||
};
|
||||
|
||||
const TransTypeTag: React.FC<Props> = ({ type }) => {
|
||||
return (
|
||||
<>
|
||||
{type === TransType.DIMENSION ? (
|
||||
<Tag color="blue">{'维度'}</Tag>
|
||||
) : type === 'metric' ? (
|
||||
<Tag color="orange">{'指标'}</Tag>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TransTypeTag;
|
||||
@@ -247,3 +247,54 @@
|
||||
color:#296DF3;
|
||||
}
|
||||
}
|
||||
|
||||
.overviewExtraContainer {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
.extraWrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
.extraStatistic {
|
||||
display: inline-flex;
|
||||
color: rgba(42, 46, 54, 0.65);
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.5714285714285714;
|
||||
list-style: none;
|
||||
.extraTitle {
|
||||
font-size: 12px;
|
||||
margin-inline-end: 6px;
|
||||
margin-block-end: 0;
|
||||
margin-bottom: 4px;
|
||||
color: rgba(42, 46, 54, 0.45);
|
||||
}
|
||||
.extraValue {
|
||||
font-size: 12px;
|
||||
color: rgba(42, 46, 54, 0.65);
|
||||
display: inline-block;
|
||||
direction: ltr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.infoTagList{
|
||||
.siteTagPlus {
|
||||
background: #fff;
|
||||
border-style: dashed;
|
||||
}
|
||||
.editTag {
|
||||
user-select: none;
|
||||
}
|
||||
.tagInput {
|
||||
width: 78px;
|
||||
margin-right: 8px;
|
||||
vertical-align: top;
|
||||
}
|
||||
[data-theme="dark"] .siteTagPlus {
|
||||
background: transparent;
|
||||
border-style: dashed;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user