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:
@@ -1,31 +1,23 @@
|
||||
import type { ActionType, ProColumns } from '@ant-design/pro-table';
|
||||
import ProTable from '@ant-design/pro-table';
|
||||
import { message, Button, Drawer, Space, Popconfirm, Modal, Card, Row, Col } from 'antd';
|
||||
import { ConsoleSqlOutlined, CoffeeOutlined } from '@ant-design/icons';
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import { message, Button, Space, Popconfirm } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import type { Dispatch } from 'umi';
|
||||
import { connect } from 'umi';
|
||||
import DataSourceCreateForm from '../Datasource/components/DataSourceCreateForm';
|
||||
import ClassDataSourceTypeModal from './ClassDataSourceTypeModal';
|
||||
import type { StateType } from '../model';
|
||||
import { getDatasourceList, deleteDatasource } from '../service';
|
||||
import DataSource from '../Datasource';
|
||||
import moment from 'moment';
|
||||
|
||||
const { Meta } = Card;
|
||||
type Props = {
|
||||
dispatch: Dispatch;
|
||||
domainManger: StateType;
|
||||
};
|
||||
|
||||
const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
|
||||
const { selectDomainId, dataBaseResultColsMap, dataBaseConfig } = domainManger;
|
||||
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
|
||||
const { selectDomainId } = domainManger;
|
||||
const [dataSourceItem, setDataSourceItem] = useState<any>();
|
||||
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
|
||||
const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false);
|
||||
const [fastModeSql, setFastModeSql] = useState<string>('');
|
||||
const [fastModeTableName, setFastModeTableName] = useState<string>('');
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
@@ -70,11 +62,7 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
|
||||
key="classEditBtn"
|
||||
onClick={() => {
|
||||
setDataSourceItem(record);
|
||||
if (record.datasourceDetail.queryType === 'table_query') {
|
||||
setDataSourceModalVisible(true);
|
||||
return;
|
||||
}
|
||||
setCreateModalVisible(true);
|
||||
setCreateDataSourceModalOpen(true);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
@@ -133,25 +121,10 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
|
||||
return resData;
|
||||
};
|
||||
|
||||
const queryDataBaseExcuteSql = (tableName: string) => {
|
||||
const sql = `select * from ${tableName}`;
|
||||
setFastModeSql(sql);
|
||||
setFastModeTableName(tableName);
|
||||
dispatch({
|
||||
type: 'domainManger/queryDataBaseExcuteSql',
|
||||
payload: {
|
||||
sql,
|
||||
domainId: selectDomainId,
|
||||
tableName,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProTable
|
||||
actionRef={actionRef}
|
||||
headerTitle="数据源列表"
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
params={{ domainId: selectDomainId }}
|
||||
@@ -179,59 +152,12 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
|
||||
onCancel={() => {
|
||||
setCreateDataSourceModalOpen(false);
|
||||
}}
|
||||
onTypeChange={(type) => {
|
||||
if (type === 'fast') {
|
||||
setDataSourceModalVisible(true);
|
||||
} else {
|
||||
setCreateModalVisible(true);
|
||||
}
|
||||
setCreateDataSourceModalOpen(false);
|
||||
dataSourceItem={dataSourceItem}
|
||||
onSubmit={() => {
|
||||
actionRef.current?.reload();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
{dataSourceModalVisible && (
|
||||
<DataSourceCreateForm
|
||||
sql={fastModeSql}
|
||||
basicInfoFormMode="fast"
|
||||
domainId={Number(selectDomainId)}
|
||||
dataSourceItem={dataSourceItem}
|
||||
onCancel={() => {
|
||||
setDataSourceModalVisible(false);
|
||||
}}
|
||||
onDataBaseTableChange={(tableName: string) => {
|
||||
queryDataBaseExcuteSql(tableName);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
setDataSourceModalVisible(false);
|
||||
setDataSourceItem(undefined);
|
||||
actionRef.current?.reload();
|
||||
}}
|
||||
createModalVisible={dataSourceModalVisible}
|
||||
/>
|
||||
)}
|
||||
{createModalVisible && (
|
||||
<Drawer
|
||||
width={'100%'}
|
||||
destroyOnClose
|
||||
title="数据源编辑"
|
||||
open={true}
|
||||
onClose={() => {
|
||||
setCreateModalVisible(false);
|
||||
setDataSourceItem(undefined);
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<DataSource
|
||||
initialValues={dataSourceItem}
|
||||
domainId={Number(selectDomainId)}
|
||||
onSubmitSuccess={() => {
|
||||
setCreateModalVisible(false);
|
||||
setDataSourceItem(undefined);
|
||||
actionRef.current?.reload();
|
||||
}}
|
||||
/>
|
||||
</Drawer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,28 +1,69 @@
|
||||
import { Modal, Card, Row, Col, Result, Button } from 'antd';
|
||||
import { Button, Drawer, Result, Modal, Card, Row, Col } from 'antd';
|
||||
import { ConsoleSqlOutlined, CoffeeOutlined } from '@ant-design/icons';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { history, connect } from 'umi';
|
||||
import type { Dispatch } from 'umi';
|
||||
import { history, connect } from 'umi';
|
||||
import DataSourceCreateForm from '../Datasource/components/DataSourceCreateForm';
|
||||
import type { StateType } from '../model';
|
||||
import DataSource from '../Datasource';
|
||||
import { IDataSource } from '../data';
|
||||
|
||||
const { Meta } = Card;
|
||||
type Props = {
|
||||
open: boolean;
|
||||
domainManger: StateType;
|
||||
onTypeChange: (type: 'fast' | 'normal') => void;
|
||||
dataSourceItem: IDataSource.IDataSourceItem;
|
||||
onTypeChange?: (type: 'fast' | 'normal') => void;
|
||||
onSubmit?: () => void;
|
||||
onCancel?: () => void;
|
||||
dispatch: Dispatch;
|
||||
domainManger: StateType;
|
||||
};
|
||||
|
||||
const ClassDataSourceTypeModal: React.FC<Props> = ({
|
||||
open,
|
||||
onTypeChange,
|
||||
onSubmit,
|
||||
dataSourceItem,
|
||||
domainManger,
|
||||
onCancel,
|
||||
dispatch,
|
||||
}) => {
|
||||
const { selectDomainId, dataBaseConfig } = domainManger;
|
||||
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
|
||||
|
||||
const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false);
|
||||
const [fastModeSql, setFastModeSql] = useState<string>('');
|
||||
|
||||
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setCreateDataSourceModalOpen(open);
|
||||
}, [open]);
|
||||
if (!dataSourceItem || !open) {
|
||||
setCreateDataSourceModalOpen(open);
|
||||
return;
|
||||
}
|
||||
if (dataSourceItem?.datasourceDetail?.queryType === 'table_query') {
|
||||
setDataSourceModalVisible(true);
|
||||
} else {
|
||||
setCreateModalVisible(true);
|
||||
}
|
||||
}, [dataSourceItem, open]);
|
||||
|
||||
const queryDataBaseExcuteSql = (tableName: string) => {
|
||||
const sql = `select * from ${tableName}`;
|
||||
setFastModeSql(sql);
|
||||
|
||||
dispatch({
|
||||
type: 'domainManger/queryDataBaseExcuteSql',
|
||||
payload: {
|
||||
sql,
|
||||
domainId: selectDomainId,
|
||||
tableName,
|
||||
},
|
||||
});
|
||||
};
|
||||
const handleCancel = () => {
|
||||
onCancel?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -30,7 +71,7 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
|
||||
open={createDataSourceModalOpen}
|
||||
onCancel={() => {
|
||||
setCreateDataSourceModalOpen(false);
|
||||
onCancel?.();
|
||||
handleCancel();
|
||||
}}
|
||||
footer={null}
|
||||
centered
|
||||
@@ -43,8 +84,10 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
|
||||
hoverable
|
||||
style={{ height: 220 }}
|
||||
onClick={() => {
|
||||
onTypeChange('fast');
|
||||
onTypeChange?.('fast');
|
||||
setCreateDataSourceModalOpen(false);
|
||||
|
||||
setDataSourceModalVisible(true);
|
||||
}}
|
||||
cover={
|
||||
<CoffeeOutlined
|
||||
@@ -59,8 +102,10 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
|
||||
<Col span={12}>
|
||||
<Card
|
||||
onClick={() => {
|
||||
onTypeChange('normal');
|
||||
onTypeChange?.('normal');
|
||||
setCreateDataSourceModalOpen(false);
|
||||
|
||||
setCreateModalVisible(true);
|
||||
}}
|
||||
hoverable
|
||||
style={{ height: 220 }}
|
||||
@@ -93,10 +138,52 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
|
||||
/>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
{dataSourceModalVisible && (
|
||||
<DataSourceCreateForm
|
||||
sql={fastModeSql}
|
||||
basicInfoFormMode="fast"
|
||||
domainId={Number(selectDomainId)}
|
||||
dataSourceItem={dataSourceItem}
|
||||
onCancel={() => {
|
||||
setDataSourceModalVisible(false);
|
||||
handleCancel();
|
||||
}}
|
||||
onDataBaseTableChange={(tableName: string) => {
|
||||
queryDataBaseExcuteSql(tableName);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
setDataSourceModalVisible(false);
|
||||
onSubmit?.();
|
||||
}}
|
||||
createModalVisible={dataSourceModalVisible}
|
||||
/>
|
||||
)}
|
||||
{createModalVisible && (
|
||||
<Drawer
|
||||
width={'100%'}
|
||||
destroyOnClose
|
||||
title="数据源编辑"
|
||||
open={true}
|
||||
onClose={() => {
|
||||
setCreateModalVisible(false);
|
||||
handleCancel();
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<DataSource
|
||||
initialValues={dataSourceItem}
|
||||
domainId={Number(selectDomainId)}
|
||||
onSubmitSuccess={() => {
|
||||
setCreateModalVisible(false);
|
||||
onSubmit?.();
|
||||
}}
|
||||
/>
|
||||
</Drawer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ domainManger }: { domainManger: StateType }) => ({
|
||||
domainManger,
|
||||
}))(ClassDataSourceTypeModal);
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { Modal, Card, Row, Col, Result, Button } from 'antd';
|
||||
import { ConsoleSqlOutlined, CoffeeOutlined } from '@ant-design/icons';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { history, connect } from 'umi';
|
||||
import type { StateType } from '../model';
|
||||
const { Meta } = Card;
|
||||
type Props = {
|
||||
open: boolean;
|
||||
domainManger: StateType;
|
||||
onTypeChange: (type: 'fast' | 'normal') => void;
|
||||
onCancel?: () => void;
|
||||
};
|
||||
|
||||
const ClassDataSourceTypeModal: React.FC<Props> = ({
|
||||
open,
|
||||
onTypeChange,
|
||||
domainManger,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { selectDomainId, dataBaseConfig } = domainManger;
|
||||
const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
|
||||
useEffect(() => {
|
||||
setCreateDataSourceModalOpen(open);
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
open={createDataSourceModalOpen}
|
||||
onCancel={() => {
|
||||
setCreateDataSourceModalOpen(false);
|
||||
onCancel?.();
|
||||
}}
|
||||
footer={null}
|
||||
centered
|
||||
closable={false}
|
||||
>
|
||||
{dataBaseConfig && dataBaseConfig.id ? (
|
||||
<Row gutter={16} style={{ marginTop: '0px' }}>
|
||||
<Col span={12}>
|
||||
<Card
|
||||
hoverable
|
||||
style={{ height: 220 }}
|
||||
onClick={() => {
|
||||
onTypeChange('fast');
|
||||
setCreateDataSourceModalOpen(false);
|
||||
}}
|
||||
cover={
|
||||
<CoffeeOutlined
|
||||
width={240}
|
||||
style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Meta title="快速创建" description="自动进行数据源可视化创建" />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Card
|
||||
onClick={() => {
|
||||
onTypeChange('normal');
|
||||
setCreateDataSourceModalOpen(false);
|
||||
}}
|
||||
hoverable
|
||||
style={{ height: 220 }}
|
||||
cover={
|
||||
<ConsoleSqlOutlined
|
||||
style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Meta title="SQL脚本" description="自定义SQL脚本创建数据源" />
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<Result
|
||||
status="warning"
|
||||
subTitle="创建数据源需要先完成数据库设置"
|
||||
extra={
|
||||
<Button
|
||||
type="primary"
|
||||
key="console"
|
||||
onClick={() => {
|
||||
history.replace(`/semanticModel/${selectDomainId}/dataBase`);
|
||||
onCancel?.();
|
||||
}}
|
||||
>
|
||||
去设置
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ domainManger }: { domainManger: StateType }) => ({
|
||||
domainManger,
|
||||
}))(ClassDataSourceTypeModal);
|
||||
@@ -8,6 +8,7 @@ import type { StateType } from '../model';
|
||||
import { SENSITIVE_LEVEL_ENUM } from '../constant';
|
||||
import { getDatasourceList, getDimensionList, deleteDimension } from '../service';
|
||||
import DimensionInfoModal from './DimensionInfoModal';
|
||||
import DimensionValueSettingModal from './DimensionValueSettingModal';
|
||||
import { ISemantic } from '../data';
|
||||
import moment from 'moment';
|
||||
import styles from './style.less';
|
||||
@@ -22,6 +23,11 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
|
||||
const [dimensionItem, setDimensionItem] = useState<ISemantic.IDimensionItem>();
|
||||
const [dataSourceList, setDataSourceList] = useState<any[]>([]);
|
||||
const [dimensionValueSettingList, setDimensionValueSettingList] = useState<
|
||||
ISemantic.IDimensionValueSettingItem[]
|
||||
>([]);
|
||||
const [dimensionValueSettingModalVisible, setDimensionValueSettingModalVisible] =
|
||||
useState<boolean>(false);
|
||||
const [pagination, setPagination] = useState({
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
@@ -141,6 +147,20 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
>
|
||||
编辑
|
||||
</a>
|
||||
<a
|
||||
key="classEditBtn"
|
||||
onClick={() => {
|
||||
setDimensionItem(record);
|
||||
setDimensionValueSettingModalVisible(true);
|
||||
if (Array.isArray(record.dimValueMaps)) {
|
||||
setDimensionValueSettingList(record.dimValueMaps);
|
||||
} else {
|
||||
setDimensionValueSettingList([]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
维度值设置
|
||||
</a>
|
||||
<Popconfirm
|
||||
title="确认删除?"
|
||||
okText="是"
|
||||
@@ -175,7 +195,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
<ProTable
|
||||
className={`${styles.classTable} ${styles.classTableSelectColumnAlignLeft}`}
|
||||
actionRef={actionRef}
|
||||
headerTitle="维度列表"
|
||||
// headerTitle="维度列表"
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
request={queryDimensionList}
|
||||
@@ -236,6 +256,26 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{dimensionValueSettingModalVisible && (
|
||||
<DimensionValueSettingModal
|
||||
dimensionValueSettingList={dimensionValueSettingList}
|
||||
open={dimensionValueSettingModalVisible}
|
||||
dimensionItem={dimensionItem}
|
||||
onCancel={() => {
|
||||
setDimensionValueSettingModalVisible(false);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
actionRef?.current?.reload();
|
||||
dispatch({
|
||||
type: 'domainManger/queryDimensionList',
|
||||
payload: {
|
||||
domainId: selectDomainId,
|
||||
},
|
||||
});
|
||||
setDimensionValueSettingModalVisible(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -166,43 +166,12 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
|
||||
},
|
||||
];
|
||||
|
||||
// const saveMetric = async (fieldsValue: any, reloadState: boolean = true) => {
|
||||
// const queryParams = {
|
||||
// domainId: selectDomainId,
|
||||
// ...fieldsValue,
|
||||
// };
|
||||
// if (queryParams.typeParams && !queryParams.typeParams.expr) {
|
||||
// message.error('度量表达式不能为空');
|
||||
// return;
|
||||
// }
|
||||
// let saveMetricQuery = creatExprMetric;
|
||||
// if (queryParams.id) {
|
||||
// saveMetricQuery = updateExprMetric;
|
||||
// }
|
||||
// const { code, msg } = await saveMetricQuery(queryParams);
|
||||
// if (code === 200) {
|
||||
// message.success('编辑指标成功');
|
||||
// setCreateModalVisible(false);
|
||||
// if (reloadState) {
|
||||
// actionRef?.current?.reload();
|
||||
// }
|
||||
// dispatch({
|
||||
// type: 'domainManger/queryMetricList',
|
||||
// payload: {
|
||||
// domainId: selectDomainId,
|
||||
// },
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
// message.error(msg);
|
||||
// };
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProTable
|
||||
className={`${styles.classTable} ${styles.classTableSelectColumnAlignLeft}`}
|
||||
actionRef={actionRef}
|
||||
headerTitle="指标列表"
|
||||
// headerTitle="指标列表"
|
||||
rowKey="id"
|
||||
search={{
|
||||
span: 4,
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { List, Collapse, Button, Input } from 'antd';
|
||||
import { uuid } from '@/utils/utils';
|
||||
|
||||
import styles from './style.less';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
const { TextArea } = Input;
|
||||
|
||||
type Props = {
|
||||
title?: string;
|
||||
defaultCollapse?: boolean;
|
||||
value?: string[];
|
||||
onChange?: (list: string[]) => void;
|
||||
};
|
||||
|
||||
type ListItem = {
|
||||
id: string;
|
||||
sql: string;
|
||||
};
|
||||
|
||||
type List = ListItem[];
|
||||
|
||||
const CommonEditList: React.FC<Props> = ({ title, defaultCollapse = false, value, onChange }) => {
|
||||
const [listDataSource, setListDataSource] = useState<List>([]);
|
||||
const [currentSql, setCurrentSql] = useState<string>('');
|
||||
const [activeKey, setActiveKey] = useState<string>();
|
||||
const [currentRecord, setCurrentRecord] = useState<ListItem>();
|
||||
|
||||
useEffect(() => {
|
||||
if (Array.isArray(value)) {
|
||||
const list = value.map((sql: string) => {
|
||||
return {
|
||||
id: uuid(),
|
||||
sql,
|
||||
};
|
||||
});
|
||||
setListDataSource(list);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
const handleListChange = (listDataSource: List) => {
|
||||
const sqlList = listDataSource.map((item) => {
|
||||
return item.sql;
|
||||
});
|
||||
onChange?.(sqlList);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.commonEditList}>
|
||||
<Collapse
|
||||
activeKey={activeKey}
|
||||
defaultActiveKey={defaultCollapse ? ['editor'] : undefined}
|
||||
onChange={() => {}}
|
||||
ghost
|
||||
>
|
||||
<Panel
|
||||
header={title}
|
||||
key="editor"
|
||||
extra={
|
||||
activeKey ? (
|
||||
<Button
|
||||
key="saveBtn"
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
if (!currentRecord && !currentSql) {
|
||||
setActiveKey(undefined);
|
||||
return;
|
||||
}
|
||||
if (currentRecord) {
|
||||
const list = [...listDataSource].map((item) => {
|
||||
if (item.id === currentRecord.id) {
|
||||
return {
|
||||
...item,
|
||||
sql: currentSql,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
setListDataSource(list);
|
||||
handleListChange(list);
|
||||
} else {
|
||||
const list = [
|
||||
...listDataSource,
|
||||
{
|
||||
id: uuid(),
|
||||
sql: currentSql,
|
||||
},
|
||||
];
|
||||
setListDataSource(list);
|
||||
handleListChange(list);
|
||||
}
|
||||
|
||||
setActiveKey(undefined);
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
key="createBtn"
|
||||
onClick={() => {
|
||||
setCurrentRecord(undefined);
|
||||
setCurrentSql('');
|
||||
setActiveKey('editor');
|
||||
}}
|
||||
>
|
||||
新增
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
showArrow={false}
|
||||
>
|
||||
<div>
|
||||
<TextArea
|
||||
placeholder="请输入推荐问题"
|
||||
value={currentSql}
|
||||
style={{ height: 150 }}
|
||||
minLength={5}
|
||||
onChange={(e) => {
|
||||
setCurrentSql(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={listDataSource || []}
|
||||
renderItem={(item) => (
|
||||
<List.Item
|
||||
actions={[
|
||||
<a
|
||||
key="list-loadmore-edit"
|
||||
onClick={() => {
|
||||
setCurrentSql(item.sql);
|
||||
setCurrentRecord(item);
|
||||
setActiveKey('editor');
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</a>,
|
||||
<a
|
||||
key="list-loadmore-more"
|
||||
onClick={() => {
|
||||
const list = [...listDataSource].filter(({ id }) => {
|
||||
return item.id !== id;
|
||||
});
|
||||
handleListChange(list);
|
||||
setListDataSource(list);
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</a>,
|
||||
]}
|
||||
>
|
||||
<List.Item.Meta title={item.sql} />
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommonEditList;
|
||||
@@ -9,7 +9,7 @@ type Props = {
|
||||
title?: string;
|
||||
tableDataSource: any[];
|
||||
columnList: any[];
|
||||
rowKey: string;
|
||||
rowKey?: string;
|
||||
editableProTableProps?: any;
|
||||
onDataSourceChange?: (dataSource: any) => void;
|
||||
extenderCtrlColumn?: (text, record, _, action) => ReactNode[];
|
||||
@@ -35,6 +35,7 @@ const CommonEditTable: React.FC<Props> = forwardRef(
|
||||
}: Props,
|
||||
ref: Ref<any>,
|
||||
) => {
|
||||
const defaultRowKey = rowKey || 'editRowId';
|
||||
const [dataSource, setDataSource] = useState<any[]>(tableDataSource);
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
@@ -50,7 +51,7 @@ const CommonEditTable: React.FC<Props> = forwardRef(
|
||||
tableDataSource.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
editRowId: item[rowKey] || (Math.random() * 1000000).toFixed(0),
|
||||
editRowId: item[defaultRowKey] || (Math.random() * 1000000).toFixed(0),
|
||||
};
|
||||
}),
|
||||
);
|
||||
@@ -82,7 +83,9 @@ const CommonEditTable: React.FC<Props> = forwardRef(
|
||||
<a
|
||||
key="deleteBtn"
|
||||
onClick={() => {
|
||||
const data = [...dataSource].filter((item) => item[rowKey] !== record[rowKey]);
|
||||
const data = [...dataSource].filter(
|
||||
(item) => item[defaultRowKey] !== record[defaultRowKey],
|
||||
);
|
||||
setDataSource(data);
|
||||
handleDataSourceChange(data);
|
||||
}}
|
||||
@@ -111,7 +114,7 @@ const CommonEditTable: React.FC<Props> = forwardRef(
|
||||
key={title}
|
||||
actionRef={actionRef}
|
||||
headerTitle={title}
|
||||
rowKey={'editRowId'}
|
||||
rowKey={defaultRowKey}
|
||||
columns={columns}
|
||||
value={dataSource}
|
||||
tableAlertRender={() => {
|
||||
@@ -133,9 +136,9 @@ const CommonEditTable: React.FC<Props> = forwardRef(
|
||||
}}
|
||||
editable={{
|
||||
onSave: (_, row) => {
|
||||
const rowKeyValue = row[rowKey];
|
||||
const rowKeyValue = row[defaultRowKey];
|
||||
const isSame = dataSource.filter((item: any, index: number) => {
|
||||
return index !== row.index && item[rowKey] === rowKeyValue;
|
||||
return index !== row.index && item[defaultRowKey] === rowKeyValue;
|
||||
});
|
||||
if (isSame[0]) {
|
||||
message.error('存在重复值');
|
||||
|
||||
@@ -20,15 +20,8 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
) => {
|
||||
const [form] = Form.useForm();
|
||||
const [selectedDbType, setSelectedDbType] = useState<string>('h2');
|
||||
// const queryDatabaseConfig = async () => {
|
||||
// const { code, data } = await getDatabaseByDomainId(domainId);
|
||||
// if (code === 200) {
|
||||
// form.setFieldsValue({ ...data });
|
||||
// setSelectedDbType(data?.type);
|
||||
// return;
|
||||
// }
|
||||
// message.error('数据库配置获取错误');
|
||||
// };
|
||||
|
||||
const [testLoading, setTestLoading] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
@@ -36,11 +29,6 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
setSelectedDbType(dataBaseConfig?.type);
|
||||
}, [dataBaseConfig]);
|
||||
|
||||
// useEffect(() => {
|
||||
// form.resetFields();
|
||||
// // queryDatabaseConfig();
|
||||
// }, [domainId]);
|
||||
|
||||
const getFormValidateFields = async () => {
|
||||
return await form.validateFields();
|
||||
};
|
||||
@@ -65,10 +53,12 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
};
|
||||
const testDatabaseConnection = async () => {
|
||||
const values = await form.validateFields();
|
||||
setTestLoading(true);
|
||||
const { code, data } = await testDatabaseConnect({
|
||||
...values,
|
||||
domainId,
|
||||
});
|
||||
setTestLoading(false);
|
||||
if (code === 200 && data) {
|
||||
message.success('连接测试通过');
|
||||
return;
|
||||
@@ -138,7 +128,9 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
<FormItem name="database" label="数据库名称">
|
||||
<Input placeholder="请输入数据库名称" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem name="version" label="数据库版本">
|
||||
<Input placeholder="请输入数据库版本" />
|
||||
</FormItem>
|
||||
<FormItem name="description" label="描述">
|
||||
<TextArea placeholder="请输入数据库描述" style={{ height: 100 }} />
|
||||
</FormItem>
|
||||
@@ -146,6 +138,7 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
loading={testLoading}
|
||||
onClick={() => {
|
||||
testDatabaseConnection();
|
||||
}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Form, Input, Modal, Select } from 'antd';
|
||||
import { SENSITIVE_LEVEL_OPTIONS } from '../constant';
|
||||
import { formLayout } from '@/components/FormHelper/utils';
|
||||
@@ -6,6 +6,7 @@ import SqlEditor from '@/components/SqlEditor';
|
||||
import InfoTagList from './InfoTagList';
|
||||
import { ISemantic } from '../data';
|
||||
import { createDimension, updateDimension } from '../service';
|
||||
// import DimensionValueSettingModal from './DimensionValueSettingModal';
|
||||
import { message } from 'antd';
|
||||
|
||||
export type CreateFormProps = {
|
||||
@@ -31,16 +32,28 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
onSubmit: handleUpdate,
|
||||
}) => {
|
||||
const isEdit = !!dimensionItem?.id;
|
||||
|
||||
const [dimensionValueSettingList, setDimensionValueSettingList] = useState<
|
||||
ISemantic.IDimensionValueSettingItem[]
|
||||
>([]);
|
||||
const [form] = Form.useForm();
|
||||
const { setFieldsValue, resetFields } = form;
|
||||
|
||||
const handleSubmit = async () => {
|
||||
// const [dimensionValueSettingModalVisible, setDimensionValueSettingModalVisible] =
|
||||
// useState<boolean>(false);
|
||||
const handleSubmit = async (
|
||||
isSilenceSubmit = false,
|
||||
dimValueMaps?: ISemantic.IDimensionValueSettingItem[],
|
||||
) => {
|
||||
const fieldsValue = await form.validateFields();
|
||||
await saveDimension(fieldsValue);
|
||||
await saveDimension(
|
||||
{
|
||||
...fieldsValue,
|
||||
dimValueMaps: dimValueMaps || dimensionValueSettingList,
|
||||
},
|
||||
isSilenceSubmit,
|
||||
);
|
||||
};
|
||||
|
||||
const saveDimension = async (fieldsValue: any) => {
|
||||
const saveDimension = async (fieldsValue: any, isSilenceSubmit = false) => {
|
||||
const queryParams = {
|
||||
domainId,
|
||||
type: 'categorical',
|
||||
@@ -52,8 +65,10 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
}
|
||||
const { code, msg } = await saveDimensionQuery(queryParams);
|
||||
if (code === 200) {
|
||||
message.success('编辑维度成功');
|
||||
handleUpdate(fieldsValue);
|
||||
if (!isSilenceSubmit) {
|
||||
message.success('编辑维度成功');
|
||||
handleUpdate(fieldsValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
message.error(msg);
|
||||
@@ -66,6 +81,11 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
useEffect(() => {
|
||||
if (dimensionItem) {
|
||||
setFormVal();
|
||||
if (Array.isArray(dimensionItem.dimValueMaps)) {
|
||||
setDimensionValueSettingList(dimensionItem.dimValueMaps);
|
||||
} else {
|
||||
setDimensionValueSettingList([]);
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
@@ -78,7 +98,12 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
return (
|
||||
<>
|
||||
<Button onClick={onCancel}>取消</Button>
|
||||
<Button type="primary" onClick={handleSubmit}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
handleSubmit();
|
||||
}}
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
</>
|
||||
@@ -93,20 +118,22 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
</FormItem>
|
||||
<FormItem
|
||||
name="name"
|
||||
label="维度中文名"
|
||||
rules={[{ required: true, message: '请输入维度中文名' }]}
|
||||
label="维度名称"
|
||||
rules={[{ required: true, message: '请输入维度名称' }]}
|
||||
>
|
||||
<Input placeholder="名称不可重复" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
hidden={isEdit}
|
||||
name="bizName"
|
||||
label="维度英文名"
|
||||
rules={[{ required: true, message: '请输入维度英文名' }]}
|
||||
label="字段名称"
|
||||
rules={[{ required: true, message: '请输入字段名称' }]}
|
||||
>
|
||||
<Input placeholder="名称不可重复" disabled={isEdit} />
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
hidden={isEdit}
|
||||
name="datasourceId"
|
||||
label="所属数据源"
|
||||
rules={[{ required: true, message: '请选择所属数据源' }]}
|
||||
@@ -158,6 +185,16 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
>
|
||||
<TextArea placeholder="请输入维度描述" />
|
||||
</FormItem>
|
||||
{/* <FormItem name="dimValueMaps" label="维度值设置">
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setDimensionValueSettingModalVisible(true);
|
||||
}}
|
||||
>
|
||||
设置
|
||||
</Button>
|
||||
</FormItem> */}
|
||||
<FormItem
|
||||
name="expr"
|
||||
label="表达式"
|
||||
@@ -171,28 +208,38 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
width={800}
|
||||
destroyOnClose
|
||||
title="维度信息"
|
||||
style={{ top: 48 }}
|
||||
maskClosable={false}
|
||||
open={bindModalVisible}
|
||||
footer={renderFooter()}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<Form
|
||||
{...formLayout}
|
||||
form={form}
|
||||
initialValues={
|
||||
{
|
||||
// ...formVals,
|
||||
}
|
||||
}
|
||||
<>
|
||||
<Modal
|
||||
width={800}
|
||||
destroyOnClose
|
||||
title="维度信息"
|
||||
style={{ top: 48 }}
|
||||
maskClosable={false}
|
||||
open={bindModalVisible}
|
||||
footer={renderFooter()}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
{renderContent()}
|
||||
</Form>
|
||||
</Modal>
|
||||
<Form {...formLayout} form={form}>
|
||||
{renderContent()}
|
||||
</Form>
|
||||
</Modal>
|
||||
{/* {dimensionValueSettingModalVisible && (
|
||||
<DimensionValueSettingModal
|
||||
dimensionValueSettingList={dimensionValueSettingList}
|
||||
open={dimensionValueSettingModalVisible}
|
||||
onCancel={() => {
|
||||
setDimensionValueSettingModalVisible(false);
|
||||
}}
|
||||
onSubmit={(dimValueMaps) => {
|
||||
if (isEdit) {
|
||||
handleSubmit(true, dimValueMaps);
|
||||
}
|
||||
setDimensionValueSettingList(dimValueMaps);
|
||||
setDimensionValueSettingModalVisible(false);
|
||||
}}
|
||||
/>
|
||||
)} */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Modal, message } from 'antd';
|
||||
import { ISemantic } from '../data';
|
||||
import CommonEditTable from './CommonEditTable';
|
||||
import { createDimension, updateDimension } from '../service';
|
||||
import { connect } from 'umi';
|
||||
import type { StateType } from '../model';
|
||||
|
||||
export type CreateFormProps = {
|
||||
dimensionValueSettingList: ISemantic.IDimensionValueSettingItem[];
|
||||
onCancel: () => void;
|
||||
dimensionItem?: ISemantic.IDimensionItem;
|
||||
open: boolean;
|
||||
onSubmit: (values?: any) => void;
|
||||
domainManger: StateType;
|
||||
};
|
||||
|
||||
type TableDataSource = { techName: string; bizName: string; alias: string };
|
||||
|
||||
const DimensionInfoModal: React.FC<CreateFormProps> = ({
|
||||
onCancel,
|
||||
open,
|
||||
dimensionItem,
|
||||
dimensionValueSettingList,
|
||||
domainManger,
|
||||
onSubmit,
|
||||
}) => {
|
||||
const [tableDataSource, setTableDataSource] = useState<TableDataSource[]>([]);
|
||||
const { selectDomainId } = domainManger;
|
||||
const [dimValueMaps, setDimValueMaps] = useState<ISemantic.IDimensionValueSettingItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const dataSource = dimensionValueSettingList.map((item) => {
|
||||
const { alias } = item;
|
||||
return {
|
||||
...item,
|
||||
alias: Array.isArray(alias) ? alias.join(',') : '',
|
||||
};
|
||||
});
|
||||
setTableDataSource(dataSource);
|
||||
setDimValueMaps(dimensionValueSettingList);
|
||||
}, [dimensionValueSettingList]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await saveDimension({ dimValueMaps });
|
||||
onSubmit?.(dimValueMaps);
|
||||
};
|
||||
|
||||
const saveDimension = async (fieldsValue: any) => {
|
||||
if (!dimensionItem?.id) {
|
||||
return;
|
||||
}
|
||||
const queryParams = {
|
||||
domainId: selectDomainId,
|
||||
id: dimensionItem.id,
|
||||
...fieldsValue,
|
||||
};
|
||||
const { code, msg } = await updateDimension(queryParams);
|
||||
if (code === 200) {
|
||||
return;
|
||||
}
|
||||
message.error(msg);
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
return (
|
||||
<>
|
||||
<Button onClick={onCancel}>取消</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
handleSubmit();
|
||||
}}
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '技术名称',
|
||||
dataIndex: 'techName',
|
||||
width: 200,
|
||||
formItemProps: {
|
||||
fieldProps: {
|
||||
placeholder: '请填写技术名称',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
whitespace: true,
|
||||
message: '此项是必填项',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '业务名称',
|
||||
dataIndex: 'bizName',
|
||||
width: 200,
|
||||
fieldProps: {
|
||||
placeholder: '请填写业务名称',
|
||||
},
|
||||
formItemProps: {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
whitespace: true,
|
||||
message: '此项是必填项',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '别名',
|
||||
dataIndex: 'alias',
|
||||
fieldProps: {
|
||||
placeholder: '多个别名用英文逗号隔开',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
width={1000}
|
||||
destroyOnClose
|
||||
title="维度值设置"
|
||||
style={{ top: 48 }}
|
||||
maskClosable={false}
|
||||
open={open}
|
||||
footer={renderFooter()}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<CommonEditTable
|
||||
tableDataSource={tableDataSource}
|
||||
columnList={columns}
|
||||
onDataSourceChange={(tableData) => {
|
||||
const dimValueMaps = tableData.map((item: TableDataSource) => {
|
||||
return {
|
||||
...item,
|
||||
alias: item.alias ? `${item.alias}`.split(',') : [],
|
||||
};
|
||||
});
|
||||
|
||||
setDimValueMaps(dimValueMaps);
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ domainManger }: { domainManger: StateType }) => ({
|
||||
domainManger,
|
||||
}))(DimensionInfoModal);
|
||||
@@ -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;
|
||||
|
||||
@@ -199,15 +199,15 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
|
||||
</FormItem>
|
||||
<FormItem
|
||||
name="name"
|
||||
label="指标中文名"
|
||||
rules={[{ required: true, message: '请输入指标中文名' }]}
|
||||
label="指标名称"
|
||||
rules={[{ required: true, message: '请输入指标名称' }]}
|
||||
>
|
||||
<Input placeholder="名称不可重复" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
name="bizName"
|
||||
label="指标英文名"
|
||||
rules={[{ required: true, message: '请输入指标英文名' }]}
|
||||
label="字段名称"
|
||||
rules={[{ required: true, message: '请输入字段名称' }]}
|
||||
>
|
||||
<Input placeholder="名称不可重复" disabled={isEdit} />
|
||||
</FormItem>
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { Tag } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
import { TransType } from '../enum';
|
||||
import { SemanticNodeType } from '../enum';
|
||||
|
||||
type Props = {
|
||||
type: TransType;
|
||||
type: SemanticNodeType;
|
||||
};
|
||||
|
||||
const TransTypeTag: React.FC<Props> = ({ type }) => {
|
||||
return (
|
||||
<>
|
||||
{type === TransType.DIMENSION ? (
|
||||
{type === SemanticNodeType.DIMENSION ? (
|
||||
<Tag color="blue">{'维度'}</Tag>
|
||||
) : type === 'metric' ? (
|
||||
) : type === SemanticNodeType.METRIC ? (
|
||||
<Tag color="orange">{'指标'}</Tag>
|
||||
) : type === SemanticNodeType.DATASOURCE ? (
|
||||
<Tag color="green">{'数据源'}</Tag>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
.projectManger {
|
||||
width: 100%;
|
||||
min-height: calc(100vh - 48px);
|
||||
background: #f8f9fb;
|
||||
// background: #f8f9fb;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -37,8 +38,17 @@
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 0 20px;
|
||||
line-height: 28px;
|
||||
:global {
|
||||
.ant-tabs-tab-btn {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
.ant-tabs-nav-wrap {
|
||||
padding: 0 20px;
|
||||
}
|
||||
.ant-tabs-nav {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mainTip {
|
||||
@@ -50,8 +60,9 @@
|
||||
padding: 0 !important;
|
||||
}
|
||||
.ant-tabs-content-holder {
|
||||
overflow: scroll;
|
||||
height: calc(100vh - 192px);
|
||||
margin-top: 20px;
|
||||
// overflow: scroll;
|
||||
// height: calc(100vh - 175px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user