[improvement][headless-fe] Revamped the interaction for semantic modeling routing and successfully implemented the switching between dimension and dataset management. (#1934)

Co-authored-by: tristanliu <tristanliu@tencent.com>
This commit is contained in:
Jun Zhang
2024-11-30 20:03:41 +08:00
committed by GitHub
parent 593597fe26
commit 82c63a7f22
42 changed files with 1889 additions and 997 deletions

View File

@@ -11,6 +11,7 @@ import styles from '../../components/style.less';
import { ISemantic } from '../../data';
import { ColumnsConfig } from '../../components/TableColumnRender';
import ViewSearchFormModal from './ViewSearchFormModal';
import { toDatasetEditPage } from '@/pages/SemanticModel/utils';
type Props = {
// dataSetList: ISemantic.IDatasetItem[];
@@ -90,9 +91,10 @@ const DataSetTable: React.FC<Props> = ({ disabledEdit = false }) => {
return (
<a
onClick={() => {
setEditFormStep(1);
setViewItem(record);
setCreateDataSourceModalOpen(true);
toDatasetEditPage(record.domainId, record.id, 'relation');
// setEditFormStep(1);
// setViewItem(record);
// setCreateDataSourceModalOpen(true);
}}
>
{name}
@@ -143,9 +145,10 @@ const DataSetTable: React.FC<Props> = ({ disabledEdit = false }) => {
<a
key="metricEditBtn"
onClick={() => {
setEditFormStep(0);
setViewItem(record);
setCreateDataSourceModalOpen(true);
toDatasetEditPage(record.domainId, record.id);
// setEditFormStep(0);
// setViewItem(record);
// setCreateDataSourceModalOpen(true);
}}
>

View File

@@ -0,0 +1,202 @@
import React, { useState, useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
import { Form, Input, Select, Spin, Space } from 'antd';
import type { Ref } from 'react';
import styles from '../../components/style.less';
import { message } from 'antd';
import { formLayout } from '@/components/FormHelper/utils';
import { createView, updateView, getDimensionList, queryMetric } from '../../service';
import { ISemantic } from '../../data';
import FormItemTitle from '@/components/FormHelper/FormItemTitle';
import SelectTMEPerson from '@/components/SelectTMEPerson';
import ViewModelConfigTransfer from './ViewModelConfigTransfer';
const FormItem = Form.Item;
export type ModelCreateFormModalProps = {
activeKey: string;
domainId: number;
datasetItem: any;
modelList: ISemantic.IModelItem[];
onCancel: () => void;
onSubmit: (values: any) => void;
};
const DatasetCreateForm: React.FC<ModelCreateFormModalProps> = forwardRef(
(
{ activeKey, datasetItem, domainId, onCancel, onSubmit, modelList }: ModelCreateFormModalProps,
ref: Ref<any>,
) => {
const [saveLoading, setSaveLoading] = useState<boolean>(false);
const [dimensionLoading, setDimensionLoading] = useState<boolean>(false);
const [selectedModelItem, setSelectedModelItem] = useState<ISemantic.IModelItem | undefined>(
modelList[0],
);
const [form] = Form.useForm();
const configTableRef = useRef<any>();
useImperativeHandle(ref, () => ({
onSave: () => {
return handleConfirm();
},
}));
useEffect(() => {
if (Array.isArray(modelList) || !selectedModelItem) {
setSelectedModelItem(modelList[0]);
}
}, [modelList]);
useEffect(() => {
form.setFieldsValue({
...datasetItem,
});
}, [datasetItem]);
const [dimensionList, setDimensionList] = useState<ISemantic.IDimensionItem[]>();
const [metricList, setMetricList] = useState<ISemantic.IMetricItem[]>();
useEffect(() => {
if (selectedModelItem?.id) {
queryDimensionList(selectedModelItem.id);
queryMetricList(selectedModelItem.id);
}
}, [selectedModelItem]);
const queryDimensionList = async (modelId: number) => {
setDimensionLoading(true);
const { code, data, msg } = await getDimensionList({ modelId });
setDimensionLoading(false);
if (code === 200 && Array.isArray(data?.list)) {
setDimensionList(data.list);
} else {
message.error(msg);
}
};
const queryMetricList = async (modelId: number) => {
const { code, data, msg } = await queryMetric({ modelId });
if (code === 200 && Array.isArray(data?.list)) {
setMetricList(data.list);
} else {
message.error(msg);
}
};
const handleConfirm = async () => {
const fieldsValue = await form.validateFields();
const viewModelConfigsMap = configTableRef?.current.getViewModelConfigs() || {};
const queryData: ISemantic.IModelItem = {
...datasetItem,
...fieldsValue,
dataSetDetail: {
dataSetModelConfigs: Object.values(viewModelConfigsMap),
},
domainId,
};
setSaveLoading(true);
const { code, msg } = await (!queryData.id ? createView : updateView)(queryData);
setSaveLoading(false);
if (code === 200) {
onSubmit?.(queryData);
} else {
message.error(msg);
}
};
const renderContent = () => {
return (
<>
<div style={{ display: activeKey === 'relation' ? 'block' : 'none' }}>
<Spin spinning={dimensionLoading}>
<ViewModelConfigTransfer
toolbarSolt={
<Space>
<span>: </span>
<Select
style={{
minWidth: 150,
textAlign: 'left',
}}
value={selectedModelItem?.id}
placeholder="请选择模型,获取当前模型下指标维度信息"
onChange={(val) => {
setDimensionList(undefined);
setMetricList(undefined);
const modelItem = modelList.find((item) => item.id === val);
setSelectedModelItem(modelItem);
}}
options={modelList.map((item) => {
return { label: item.name, value: item.id };
})}
/>
</Space>
}
dimensionList={dimensionList}
metricList={metricList}
modelItem={selectedModelItem}
viewItem={datasetItem}
ref={configTableRef}
/>
</Spin>
</div>
<div style={{ display: activeKey === 'basic' ? 'block' : 'none' }}>
<FormItem
name="name"
label="数据集名称"
rules={[{ required: true, message: '请输入数据集名称!' }]}
>
<Input placeholder="数据集名称不可重复" />
</FormItem>
<FormItem
name="bizName"
label="数据集英文名称"
rules={[{ required: true, message: '请输入数据集英文名称!' }]}
>
<Input placeholder="请输入数据集英文名称" />
</FormItem>
{/* <FormItem
name="alias"
label="别名"
getValueFromEvent={(value) => {
return Array.isArray(value) ? value.join(',') : '';
}}
getValueProps={(value) => {
return {
value: isString(value) ? value.split(',') : [],
};
}}
>
<Select
mode="tags"
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
tokenSeparators={[',']}
maxTagCount={9}
/>
</FormItem> */}
<FormItem name="admins" label={<FormItemTitle title={'责任人'} />}>
<SelectTMEPerson placeholder="请邀请团队成员" />
</FormItem>
<FormItem name="description" label="数据集描述">
<Input.TextArea placeholder="数据集描述" />
</FormItem>
</div>
</>
);
};
return (
<>
<Form
{...formLayout}
form={form}
onValuesChange={(value, values) => {}}
className={styles.form}
>
{renderContent()}
</Form>
</>
);
},
);
export default DatasetCreateForm;

View File

@@ -0,0 +1,122 @@
import { message, Form } from 'antd';
import React, { useState, useEffect, useRef } from 'react';
import { useParams, useModel, Helmet } from '@umijs/max';
import { BASE_TITLE } from '@/common/constants';
import { ISemantic } from '../../data';
import { getAllModelByDomainId, getDataSetDetail } from '../../service';
import DetailContainer from '@/pages/SemanticModel/components/DetailContainer';
import DetailSider from '@/pages/SemanticModel/components/DetailContainer/DetailSider';
import { ProjectOutlined, ConsoleSqlOutlined } from '@ant-design/icons';
import DatasetCreateForm from './DatasetCreateForm';
import DetailFormWrapper from '@/pages/SemanticModel/components/DetailContainer/DetailFormWrapper';
type Props = Record<string, any>;
const DataSetDetail: React.FC<Props> = () => {
const settingList = [
{
icon: <ProjectOutlined />,
key: 'basic',
text: '基本信息',
},
{
icon: <ConsoleSqlOutlined />,
key: 'relation',
text: '关联信息',
},
];
const params: any = useParams();
const detailId = params.datasetId;
const menuKey = params.menuKey;
const [detailData, setDetailData] = useState<ISemantic.IDatasetItem>();
const domainModel = useModel('SemanticModel.domainData');
const { selectDomainId, setSelectDataSet } = domainModel;
const [modelList, setModelList] = useState<ISemantic.IModelItem[]>([]);
const [activeMenu, setActiveMenu] = useState<any>(() => {
if (menuKey) {
const target = settingList.find((item) => item.key === menuKey);
if (target) {
return target;
}
}
return settingList[0];
});
const detailFormRef = useRef<any>();
useEffect(() => {
return () => {
setSelectDataSet(undefined);
};
}, []);
useEffect(() => {
if (!detailId) {
return;
}
queryDetailData(detailId);
}, [detailId]);
useEffect(() => {
if (!selectDomainId) {
return;
}
queryDomainAllModel();
}, [selectDomainId]);
const queryDomainAllModel = async () => {
const { code, data, msg } = await getAllModelByDomainId(selectDomainId);
if (code === 200) {
setModelList(data);
} else {
message.error(msg);
}
};
const queryDetailData = async (id: number) => {
const { code, data, msg } = await getDataSetDetail(id);
if (code === 200) {
setDetailData(data);
setSelectDataSet(data);
return;
}
message.error(msg);
};
return (
<>
<Helmet title={`[数据集]${detailData?.name}-${BASE_TITLE}`} />
<DetailContainer
siderNode={
<DetailSider
menuKey={activeMenu.key}
menuList={settingList}
detailData={detailData}
onMenuKeyChange={(key: string, menu) => {
// setSettingKey(key);
setActiveMenu(menu);
}}
/>
}
containerNode={
<DetailFormWrapper
currentMenu={activeMenu}
onSave={() => {
detailFormRef.current.onSave();
}}
>
<DatasetCreateForm
ref={detailFormRef}
activeKey={activeMenu.key}
domainId={selectDomainId}
datasetItem={detailData}
modelList={modelList}
/>
</DetailFormWrapper>
}
/>
</>
);
};
export default DataSetDetail;

View File

@@ -165,7 +165,6 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
</>
);
};
const renderContent = () => {
return (
<>
@@ -262,7 +261,6 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
<Steps style={{ marginBottom: 28 }} size="small" current={currentStep}>
<Step title="基本信息" />
<Step title="关联信息" />
{/* <Step title="进阶设置" /> */}
</Steps>
<Form
{...formLayout}

View File

@@ -161,7 +161,7 @@ const ViewModelConfigTransfer: React.FC<Props> = forwardRef(
setSelectedTransferKeys(transferKeys);
setViewModelConfigsMap(viewConfigMap);
}
}, [queryType]);
}, [queryType, viewItem]);
useEffect(() => {
if (queryType !== TransType.METRIC) {