[improvement][semantic-fe] Add model alias setting & Add view permission restrictions to the model permission management tab. (#63)

[improvement][semantic-fe] Add permission control to the action buttons for the main domain; apply high sensitivity filtering to the authorization of metrics/dimensions.
[improvement][semantic-fe] Optimize the editing mode in the dimension/metric/datasource components to use the modelId stored in the database for data, instead of relying on the data from the state manager.
This commit is contained in:
tristanliu
2023-09-09 18:36:54 +08:00
committed by GitHub
parent 71d9f9c9e9
commit 5bab18e092
18 changed files with 91 additions and 98 deletions

View File

@@ -34,17 +34,11 @@ const ROUTES = [
component: './Agent',
envEnableList: [ENV_KEY.CHAT],
},
{
{
path: '/model',
name: 'semanticModel',
component: './SemanticModel/DomainManager',
envEnableList: [ENV_KEY.SEMANTIC],
routes: [
{
path: '/model',
redirect: '/model/:domainId?/:modelId?/:menuKey?',
},
{
path: '/model/:domainId?/:modelId?/:menuKey?',
component: './SemanticModel/DomainManager',
@@ -59,6 +53,7 @@ const ROUTES = [
},
],
},
{
path: '/database',
name: 'database',
@@ -66,7 +61,6 @@ const ROUTES = [
component: './SemanticModel/components/Database/DatabaseTable',
envEnableList: [ENV_KEY.SEMANTIC],
},
{
path: '/metric',
name: 'metric',

View File

@@ -18,7 +18,7 @@ const GlobalHeaderRight: React.FC = () => {
const { navTheme, layout } = initialState.settings;
let className = styles.right;
if ((navTheme === 'dark' && layout === 'top') || layout === 'mix') {
if (layout === 'top' || layout === 'mix') {
className = cx(styles.right, styles.dark);
}

View File

@@ -4,19 +4,21 @@ import DataSourceBasicForm from './DataSourceBasicForm';
import FieldForm from './DataSourceFieldForm';
import { formLayout } from '@/components/FormHelper/utils';
import { EnumDataSourceType } from '../constants';
import type { DataInstanceItem } from '../data';
// import type { DataInstanceItem } from '../data';
import styles from '../style.less';
import { createDatasource, updateDatasource, getColumns } from '../../service';
import type { Dispatch } from 'umi';
import type { StateType } from '../../model';
import { connect } from 'umi';
import { IDataSource } from '../../data';
export type CreateFormProps = {
domainManger: StateType;
dispatch: Dispatch;
createModalVisible: boolean;
sql?: string;
dataSourceItem: DataInstanceItem | any;
databaseItem?: any;
dataSourceItem: IDataSource.IDataSourceItem;
onCancel?: () => void;
onSubmit?: (dataSourceInfo: any) => void;
scriptColumns?: any[] | undefined;
@@ -39,6 +41,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
sql = '',
onSubmit,
dataSourceItem,
databaseItem,
basicInfoFormMode,
}) => {
const isEdit = !!dataSourceItem?.id;
@@ -160,10 +163,10 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
const queryParams = {
...submitForm,
sqlQuery: sql,
databaseId: dataSourceItem?.databaseId || formDatabaseId,
databaseId: dataSourceItem?.databaseId || formDatabaseId || databaseItem?.key,
queryType: basicInfoFormMode === 'fast' ? 'table_query' : 'sql_query',
tableQuery: dbName && tableName ? `${dbName}.${tableName}` : '',
modelId,
modelId: isEdit ? dataSourceItem.modelId : modelId,
};
const queryDatasource = isEdit ? updateDatasource : createDatasource;
const { code, msg, data } = await queryDatasource(queryParams);
@@ -303,7 +306,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
return (
<>
<div style={{ display: currentStep === 1 ? 'block' : 'none' }}>
<FieldForm fields={fields} onFieldChange={handleFieldChange} />;
<FieldForm fields={fields} onFieldChange={handleFieldChange} />
</div>
<div style={{ display: currentStep !== 1 ? 'block' : 'none' }}>
<DataSourceBasicForm

View File

@@ -16,7 +16,7 @@ import {
import { isFunction } from 'lodash';
import FullScreen from '@/components/FullScreen';
import SqlEditor from '@/components/SqlEditor';
import type { TaskResultItem, DataInstanceItem, TaskResultColumn } from '../data';
import type { TaskResultItem, TaskResultColumn } from '../data';
import { excuteSql } from '@/pages/SemanticModel/service';
import DataSourceCreateForm from './DataSourceCreateForm';
import type { Dispatch } from 'umi';
@@ -36,7 +36,6 @@ type IProps = {
onUpdateSql?: (sql: string) => void;
sql?: string;
onSubmitSuccess?: (dataSourceInfo: any) => void;
onJdbcSourceChange?: (jdbcId: number) => void;
};
type ResultTableItem = Record<string, any>;
@@ -60,7 +59,6 @@ const SqlDetail: React.FC<IProps> = ({
onSubmitSuccess,
sql = '',
onUpdateSql,
onJdbcSourceChange,
}) => {
const { databaseConfigList, selectModelId: modelId } = domainManger;
const [resultTable, setResultTable] = useState<ResultTableItem[]>([]);
@@ -101,20 +99,6 @@ const SqlDetail: React.FC<IProps> = ({
const [scriptColumns, setScriptColumns] = useState<any[]>([]);
// useEffect(() => {
// const list = databaseConfigList.map((item: ISemantic.IDatabaseItem) => {
// return {
// label: item.name,
// key: item.id,
// disabled: !item.hasUsePermission,
// };
// });
// setJdbcSourceItems(list);
// const config = list[0];
// setCurrentJdbcSourceItem(config);
// onJdbcSourceChange?.(config?.key && Number(config?.key));
// }, [databaseConfigList]);
useEffect(() => {
const list = databaseConfigList.map((item: ISemantic.IDatabaseItem) => {
return {
@@ -133,9 +117,12 @@ const SqlDetail: React.FC<IProps> = ({
}
}
setCurrentJdbcSourceItem(targetDataBase);
// onJdbcSourceChange?.(targetDataBase?.key && Number(targetDataBase?.key));
}, [dataSourceItem, databaseConfigList]);
useEffect(() => {
setRunState(undefined);
}, [currentJdbcSourceItem]);
function creatCalcItem(key: string, data: string) {
const line = document.createElement('div'); // 需要每条数据一行,这样避免数据换行的时候获得的宽度不准确
const child = document.createElement('span');
@@ -408,9 +395,7 @@ const SqlDetail: React.FC<IProps> = ({
return item.key === Number(value);
})[0];
if (target) {
// setJdbcSourceName(target.label);
setCurrentJdbcSourceItem(target);
onJdbcSourceChange?.(Number(value));
}
},
}}
@@ -520,6 +505,7 @@ const SqlDetail: React.FC<IProps> = ({
{dataSourceModalVisible && (
<DataSourceCreateForm
sql={sql}
databaseItem={currentJdbcSourceItem}
dataSourceItem={dataSourceItem}
scriptColumns={scriptColumns}
onCancel={() => {

View File

@@ -43,8 +43,6 @@ const SqlSide: React.FC<Props> = ({ initialValues, onSubmitSuccess }) => {
const tableRef: TableRef = useRef();
const panesRef = useRef<Panes[]>(defaultPanes);
const [dataSourceItem, setDataSourceItem] = useState<any>(initialValues || {});
const updatePane = (list: Panes[]) => {
setPanes(list);
panesRef.current = list;
@@ -96,16 +94,10 @@ const SqlSide: React.FC<Props> = ({ initialValues, onSubmitSuccess }) => {
>
<SqlDetail
onSubmitSuccess={onSubmitSuccess}
dataSourceItem={dataSourceItem}
dataSourceItem={initialValues}
onUpdateSql={(sql: string) => {
updateTabSql(sql, pane.key);
}}
onJdbcSourceChange={(databaseId) => {
setDataSourceItem({
...dataSourceItem,
databaseId,
});
}}
sql={pane.sql}
/>
</TabPane>

View File

@@ -101,7 +101,6 @@ const XflowJsonSchemaFormDrawerForm: React.FC<CreateFormProps> = (props) => {
{dataSourceModalVisible && (
<DataSourceCreateForm
basicInfoFormMode="fast"
domainId={Number(selectDomainId)}
dataSourceItem={dataSourceItem}
onCancel={() => {
setDataSourceModalVisible(false);

View File

@@ -113,15 +113,15 @@ const NodeInfoDrawer: React.FC<Props> = ({
{
title: '应用信息',
children: [
{
label: '全路径',
value: fullPath,
content: (
<Paragraph style={{ width: 275, margin: 0 }} ellipsis={{ tooltip: fullPath }}>
{fullPath}
</Paragraph>
),
},
// {
// label: '全路径',
// value: fullPath,
// content: (
// <Paragraph style={{ width: 275, margin: 0 }} ellipsis={{ tooltip: fullPath }}>
// {fullPath}
// </Paragraph>
// ),
// },
{
label: '敏感度',
value: SENSITIVE_LEVEL_ENUM[sensitiveLevel],

View File

@@ -124,7 +124,6 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
<DataSourceCreateForm
sql={fastModeSql}
basicInfoFormMode="fast"
domainId={Number(selectDomainId)}
dataSourceItem={dataSourceItem}
onCancel={() => {
setDataSourceModalVisible(false);

View File

@@ -57,7 +57,7 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
const saveDimension = async (fieldsValue: any, isSilenceSubmit = false) => {
const queryParams = {
modelId,
modelId: isEdit ? dimensionItem.modelId : modelId,
type: 'categorical',
...fieldsValue,
};

View File

@@ -115,7 +115,7 @@ const DomainListTree: FC<DomainListProps> = ({
};
const titleRender = (node: any) => {
const { id, name, path } = node as any;
const { id, name, path, hasEditPermission } = node as any;
return (
<div className={styles.projectItem}>
<span
@@ -126,7 +126,7 @@ const DomainListTree: FC<DomainListProps> = ({
>
{name}
</span>
{createDomainBtnVisible && (
{createDomainBtnVisible && hasEditPermission && (
<span className={styles.operation}>
{Array.isArray(path) && path.length < 2 && (
<PlusOutlined

View File

@@ -33,12 +33,13 @@ const DomainManagerTab: React.FC<Props> = ({
isModel,
activeKey,
modelList,
domainManger,
handleModelChange,
onBackDomainBtnClick,
onMenuChange,
}) => {
const defaultTabKey = 'xflow';
const { selectDomainId, domainList } = domainManger;
const tabItem = [
{
label: '模型',
@@ -62,7 +63,13 @@ const DomainManagerTab: React.FC<Props> = ({
key: 'permissonSetting',
children: <PermissionSection permissionTarget={'domain'} />,
},
];
].filter((item) => {
const target = domainList.find((domain) => domain.id === selectDomainId);
if (target?.hasEditPermission) {
return true;
}
return item.key !== 'permissonSetting';
});
const isModelItem = [
{

View File

@@ -28,11 +28,10 @@ const FormItem = Form.Item;
const Option = Select.Option;
const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
{ metricList, dimensionList, domainId, entityData, chatConfigKey, chatConfigType, onSubmit },
{ metricList, dimensionList, entityData, chatConfigKey, chatConfigType, onSubmit },
ref,
) => {
const [form] = Form.useForm();
const [metricListOptions, setMetricListOptions] = useState<any>([]);
const [dataItemListOptions, setDataItemListOptions] = useState<any>([]);
const formatEntityData = formatRichEntityDataListToIds(entityData);
const getFormValidateFields = async () => {
@@ -74,16 +73,6 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
});
};
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) => {
@@ -141,9 +130,10 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
...formatEntityData,
chatDefaultConfig: { ...values, ...dimensionConfig },
};
const { modelId } = entityData;
const { code, msg, data } = await saveDomainExtendQuery({
[chatConfigKey]: params,
// domainId,
modelId,
id,
});
if (code === 200) {

View File

@@ -81,7 +81,7 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
if (globalKnowledgeConfigFormFields) {
globalKnowledgeConfig = globalKnowledgeConfigFormFields;
}
const { id } = entityData;
const { id, modelId } = entityData;
let saveDomainExtendQuery = addDomainExtend;
if (id) {
saveDomainExtendQuery = editDomainExtend;
@@ -126,8 +126,8 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
const { code, msg } = await saveDomainExtendQuery({
[chatConfigKey]: params,
// domainId,
id,
modelId,
});
if (code === 200) {
if (!isSilenceSubmit) {

View File

@@ -8,7 +8,7 @@ type Props = {
const FormLabelRequire: React.FC<Props> = ({ title, labelStyles = {} }) => {
return (
<>
<div className="ant-col ant-form-item-label">
<div className="ant-col ant-form-item-label" style={{ padding: 0 }}>
<label
htmlFor="description"
className="ant-form-item-required"

View File

@@ -136,7 +136,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
sensitiveLevel,
description,
// isPercent,
dataFormatType,
dataFormatType: dataFormatType || '',
alias: alias && alias.trim() ? alias.split(',') : [],
dataFormat: dataFormat || {
decimalPlaces: 2,
@@ -163,7 +163,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
const saveMetric = async (fieldsValue: any) => {
const queryParams = {
modelId,
modelId: isEdit ? metricItem.modelId : modelId,
...fieldsValue,
};
const { typeParams, alias, dataFormatType } = queryParams;

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
import { Form, Button, Modal, Input, Switch } from 'antd';
import { Form, Button, Modal, Input, Switch, Select } from 'antd';
import styles from './style.less';
import { message } from 'antd';
import { formLayout } from '@/components/FormHelper/utils';
@@ -22,13 +22,20 @@ const ModelCreateFormModal: React.FC<ModelCreateFormModalProps> = (props) => {
const [form] = Form.useForm();
useEffect(() => {
form.setFieldsValue(basicInfo);
form.setFieldsValue({
...basicInfo,
alias: basicInfo?.alias && basicInfo.alias.trim() ? basicInfo.alias.split(',') : [],
});
}, [basicInfo]);
const handleConfirm = async () => {
const fieldsValue = await form.validateFields();
const columnsValue = { ...fieldsValue, isUnique: 1, domainId };
const submitData = { ...formVals, ...columnsValue };
const submitData = {
...formVals,
...columnsValue,
alias: Array.isArray(fieldsValue.alias) ? fieldsValue.alias.join(',') : '',
};
setFormVals(submitData);
setSaveLoading(true);
const { code, msg } = await (!submitData.id ? createModel : updateModel)(submitData);
@@ -81,6 +88,14 @@ const ModelCreateFormModal: React.FC<ModelCreateFormModalProps> = (props) => {
>
<Input placeholder="请输入模型英文名称" />
</FormItem>
<FormItem name="alias" label="别名">
<Select
mode="tags"
placeholder="输入别名后回车确认,多别名输入、复制粘贴支持英文逗号自动分隔"
tokenSeparators={[',']}
maxTagCount={9}
/>
</FormItem>
<FormItem name="description" label="模型描述">
<Input.TextArea placeholder="模型描述" />
</FormItem>

View File

@@ -141,24 +141,28 @@ const PermissionCreateDrawer: React.FC<Props> = ({
<DimensionMetricVisibleTransfer
titles={['未授权维度/指标', '已授权维度/指标']}
sourceList={[
...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),
};
}),
...dimensionList
.map((item) => {
const transType = TransType.DIMENSION;
const { id } = item;
return {
...item,
transType,
key: wrapperTransTypeAndId(transType, id),
};
})
.filter((item) => item.sensitiveLevel === 2),
...metricList
.map((item) => {
const transType = TransType.METRIC;
const { id } = item;
return {
...item,
transType,
key: wrapperTransTypeAndId(transType, id),
};
})
.filter((item) => item.sensitiveLevel === 2),
]}
targetList={selectedKeyList}
onChange={(newTargetKeys: string[]) => {

View File

@@ -60,6 +60,7 @@ export declare namespace IDataSource {
name: string;
bizName: string;
description: string;
modelId: number;
status: number | null;
sensitiveLevel: SensitiveLevel;
domainId: number;
@@ -79,6 +80,7 @@ export declare namespace ISemantic {
name: string;
bizName: string;
description: any;
hasEditPermission: boolean;
status?: number;
typeEnum?: any;
sensitiveLevel?: number;
@@ -135,6 +137,7 @@ export declare namespace ISemantic {
expr: string;
fullPath: string;
datasourceId: number;
modelId: number;
datasourceName: string;
datasourceBizName: string;
semanticType: string;
@@ -178,6 +181,7 @@ export declare namespace ISemantic {
sensitiveLevel: number;
domainId: number;
domainName: string;
modelId: number;
type: string;
typeParams: ITypeParams;
fullPath: string;