[improvement][semantic-fe] Replacing the single status update API for indicators/dimensions with a batch update API (#327)

* [improvement][semantic-fe] Add model alias setting & Add view permission restrictions to the model permission management tab.
[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.

* [improvement][semantic-fe] Add time granularity setting in the data source configuration.

* [improvement][semantic-fe] Dictionary import for dimension values supported in Q&A visibility

* [improvement][semantic-fe] Modification of data source creation prompt wording"

* [improvement][semantic-fe] metric market experience optimization

* [improvement][semantic-fe] enhance the analysis of metric trends

* [improvement][semantic-fe] optimize the presentation of metric trend permissions

* [improvement][semantic-fe] add metric trend download functionality

* [improvement][semantic-fe] fix the dimension initialization issue in metric correlation

* [improvement][semantic-fe] Fix the issue of database changes not taking effect when creating based on an SQL data source.

* [improvement][semantic-fe] Optimizing pagination logic and some CSS styles

* [improvement][semantic-fe] Fixing the API for the indicator list by changing "current" to "pageNum"

* [improvement][semantic-fe] Fixing the default value setting for the indicator list

* [improvement][semantic-fe] Adding batch operations for indicators/dimensions/models

* [improvement][semantic-fe] Replacing the single status update API for indicators/dimensions with a batch update API
This commit is contained in:
tristanliu
2023-11-05 22:22:20 -06:00
committed by GitHub
parent 910384d17f
commit f5f9c0314a
10 changed files with 284 additions and 187 deletions

View File

@@ -21,6 +21,7 @@ import { ProCard } from '@ant-design/pro-card';
type Props = { type Props = {
disabledAdvanceSetting?: boolean; disabledAdvanceSetting?: boolean;
initialValues?: any; initialValues?: any;
showCurrentDataRangeString?: boolean;
onDateRangeChange: (value: string[], from: any) => void; onDateRangeChange: (value: string[], from: any) => void;
onDateRangeTypeChange?: (dateRangeType: DateRangeType) => void; onDateRangeTypeChange?: (dateRangeType: DateRangeType) => void;
}; };
@@ -29,6 +30,7 @@ const { CheckableTag } = Tag;
const MDatePicker: React.FC<Props> = ({ const MDatePicker: React.FC<Props> = ({
disabledAdvanceSetting, disabledAdvanceSetting,
initialValues, initialValues,
showCurrentDataRangeString = true,
onDateRangeChange, onDateRangeChange,
onDateRangeTypeChange, onDateRangeTypeChange,
}: any) => { }: any) => {
@@ -420,7 +422,8 @@ const MDatePicker: React.FC<Props> = ({
} }
/> />
</Popover> </Popover>
{!( {showCurrentDataRangeString &&
!(
currentDateSettingType === DateSettingType.STATIC && currentDateSettingType === DateSettingType.STATIC &&
currentDateMode === DateMode.RANGE && currentDateMode === DateMode.RANGE &&
dateRangeType === DateRangeType.DAY dateRangeType === DateRangeType.DAY

View File

@@ -80,6 +80,7 @@ const DebounceSelect = forwardRef(
if (disabledSearch) { if (disabledSearch) {
return; return;
} }
console.log(!allowEmptyValue && !value, value, allowEmptyValue, 333);
if (!allowEmptyValue && !value) return; if (!allowEmptyValue && !value) return;
fetchRef.current += 1; fetchRef.current += 1;
const fetchId = fetchRef.current; const fetchId = fetchRef.current;
@@ -116,9 +117,13 @@ const DebounceSelect = forwardRef(
showSearch showSearch
allowClear allowClear
mode="multiple" mode="multiple"
onClear={() => { // onClear={() => {
setOptions([]); // if (autoInit) {
}} // loadOptions('', true);
// } else {
// setOptions([]);
// }
// }}
onSearch={debounceFetcher} onSearch={debounceFetcher}
{...props} {...props}
filterOption={false} // 保持对props中filterOption属性的复写不可变更位置 filterOption={false} // 保持对props中filterOption属性的复写不可变更位置

View File

@@ -1,11 +1,10 @@
import { Form, Input, Select, Space, Row, Col, Switch, Tag } from 'antd'; import { Form, Select, Space, Tag, Button, Tooltip } from 'antd';
import StandardFormRow from '@/components/StandardFormRow'; import { PlusOutlined } from '@ant-design/icons';
import TagSelect from '@/components/TagSelect';
import React, { useEffect, useState, useRef } from 'react'; import React, { useEffect, useState, useRef } from 'react';
import { SENSITIVE_LEVEL_OPTIONS } from '../../constant';
import { SearchOutlined } from '@ant-design/icons';
import RemoteSelect, { RemoteSelectImperativeHandle } from '@/components/RemoteSelect'; import RemoteSelect, { RemoteSelectImperativeHandle } from '@/components/RemoteSelect';
import { queryDimValue } from '@/pages/SemanticModel/service'; import { queryDimValue } from '@/pages/SemanticModel/service';
import { OperatorEnum } from '@/pages/SemanticModel/enum';
import { isString } from 'lodash';
import styles from '../style.less'; import styles from '../style.less';
const FormItem = Form.Item; const FormItem = Form.Item;
@@ -13,24 +12,46 @@ const FormItem = Form.Item;
type Props = { type Props = {
dimensionOptions: { value: string; label: string }[]; dimensionOptions: { value: string; label: string }[];
modelId: number; modelId: number;
initFilterValues?: any; value?: FormData;
onFiltersChange: (_: any, values: any) => void; onChange?: (value: FormData) => void;
};
export type FormData = {
dimensionBizName: string;
operator: OperatorEnum;
dimensionValue: string | string[];
}; };
const MetricTrendDimensionFilter: React.FC<Props> = ({ const MetricTrendDimensionFilter: React.FC<Props> = ({
dimensionOptions, dimensionOptions,
modelId, modelId,
initFilterValues = {}, value,
onFiltersChange, onChange,
}) => { }) => {
// const [form] = Form.useForm(); const [form] = Form.useForm();
const dimensionValueSearchRef = useRef<RemoteSelectImperativeHandle>(); const dimensionValueSearchRef = useRef<RemoteSelectImperativeHandle>();
const queryParams = useRef<{ dimensionBizName?: string }>({}); const queryParams = useRef<{ dimensionBizName?: string }>({});
const [dimensionValue, setDimensionValue] = useState<string>('');
// const [queryParams, setQueryParams] = useState<any>({}); const [formData, setFormData] = useState<FormData>({ operator: OperatorEnum.IN } as FormData);
useEffect(() => {
if (!value) {
return;
}
form.setFieldsValue(value);
setFormData(value);
}, [value]);
useEffect(() => {
if (formData.dimensionBizName) {
queryParams.current = { dimensionBizName: formData.dimensionBizName };
dimensionValueSearchRef.current?.emitSearch('');
}
}, [formData.dimensionBizName]);
const loadSiteName = async (searchValue: string) => { const loadSiteName = async (searchValue: string) => {
if (!queryParams.current?.dimensionBizName) { if (!queryParams.current?.dimensionBizName) {
// return [];
return; return;
} }
const { dimensionBizName } = queryParams.current; const { dimensionBizName } = queryParams.current;
@@ -48,9 +69,35 @@ const MetricTrendDimensionFilter: React.FC<Props> = ({
} }
return []; return [];
}; };
const multipleValueOperator = [OperatorEnum.IN];
return ( return (
<Form
layout="inline"
form={form}
colon={false}
initialValues={{
...formData,
}}
onValuesChange={(value, values) => {
const { operator, dimensionValue } = values;
if (multipleValueOperator.includes(operator) && isString(dimensionValue)) {
const tempDimensionValue = [dimensionValue];
setFormData({ ...values, dimensionValue: tempDimensionValue });
form.setFieldValue('dimensionValue', tempDimensionValue);
return;
}
if (!multipleValueOperator.includes(operator) && Array.isArray(dimensionValue)) {
const tempDimensionValue = dimensionValue[0];
setFormData({ ...values, dimensionValue: tempDimensionValue });
form.setFieldValue('dimensionValue', tempDimensionValue);
return;
}
setFormData(values);
}}
>
<Space> <Space>
<FormItem name="dimensionBizName" noStyle>
<Select <Select
style={{ minWidth: 150 }} style={{ minWidth: 150 }}
options={dimensionOptions} options={dimensionOptions}
@@ -60,28 +107,44 @@ const MetricTrendDimensionFilter: React.FC<Props> = ({
} }
allowClear allowClear
placeholder="请选择筛选维度" placeholder="请选择筛选维度"
onChange={(value) => {
queryParams.current = { dimensionBizName: value };
if (value) {
dimensionValueSearchRef.current?.emitSearch('');
}
}}
/> />
<Tag color="processing" style={{ margin: 0 }}> </FormItem>
= <Tag color="processing" style={{ margin: 0, padding: 0 }}>
<FormItem name="operator" noStyle>
<Select
style={{ minWidth: 72 }}
bordered={false}
options={Object.values(OperatorEnum).map((operator: string) => {
return {
value: operator,
label: operator,
};
})}
/>
</FormItem>
</Tag> </Tag>
<FormItem name="dimensionValue" noStyle>
<RemoteSelect <RemoteSelect
value={dimensionValue} placeholder={formData.dimensionBizName ? '请输入维度值搜索' : '请先选择一个维度'}
onChange={(value) => {
setDimensionValue(value);
}}
ref={dimensionValueSearchRef} ref={dimensionValueSearchRef}
style={{ minWidth: 150 }} style={{ minWidth: 150 }}
mode={undefined} maxTagCount={3}
placeholder="维度值搜索" mode={multipleValueOperator.includes(formData.operator) ? 'multiple' : undefined}
fetchOptions={loadSiteName} fetchOptions={loadSiteName}
/> />
</FormItem>
<Tooltip title={'添加筛选条件'}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={() => {
const formValues = form.getFieldsValue();
onChange?.(formValues);
}}
/>
</Tooltip>
</Space> </Space>
</Form>
); );
}; };

View File

@@ -0,0 +1,50 @@
import { Form, Select, Space, Tag, Button, Tooltip } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import React, { useEffect, useState, useRef } from 'react';
import RemoteSelect, { RemoteSelectImperativeHandle } from '@/components/RemoteSelect';
import { queryDimValue } from '@/pages/SemanticModel/service';
import { OperatorEnum } from '@/pages/SemanticModel/enum';
import MetricTrendDimensionFilter from './MetricTrendDimensionFilter';
import type { FormData } from './MetricTrendDimensionFilter';
import { isString } from 'lodash';
import styles from '../style.less';
const FormItem = Form.Item;
type Props = {
dimensionOptions: { value: string; label: string }[];
modelId: number;
value?: FormData;
onChange?: (value: FormData) => void;
};
const MetricTrendDimensionFilterContainer: React.FC<Props> = ({
dimensionOptions,
modelId,
value,
onChange,
}) => {
const [filterData, setFilterData] = useState<FormData[]>([]);
return (
<div>
<MetricTrendDimensionFilter
modelId={modelId}
dimensionOptions={dimensionOptions}
value={value}
onChange={(value) => {
setFilterData([...filterData, value]);
onChange?.(value);
}}
/>
<div>
<Tag>
{filterData.map((item: FormData) => {
return <Tag key={item.dimensionValue}>{item.dimensionValue}</Tag>;
})}
</Tag>
</div>
</div>
);
};
export default MetricTrendDimensionFilterContainer;

View File

@@ -147,11 +147,24 @@ const MetricTrendSection: React.FC<Props> = ({ nodeData }) => {
return ( return (
<> <>
<div style={{ marginBottom: 5, display: 'grid', gap: 10 }}> <div style={{ marginBottom: 25 }}>
{/* <StandardFormRow key="showType" title="维度下钻" block> <Row>
<FormItem name="showType" valuePropName="checked"> <Col flex="1 1 200px">
<Form
layout="inline"
// form={form}
colon={false}
onValuesChange={(value, values) => {
if (value.key) {
return;
}
// handleValuesChange(value, values);
}}
>
{/* <StandardFormRow key="dimensionSelected" title="维度下钻:">
<FormItem name="dimensionSelected">
<Select <Select
style={{ minWidth: 150 }} style={{ minWidth: 150, maxWidth: 200 }}
options={relationDimensionOptions} options={relationDimensionOptions}
showSearch showSearch
filterOption={(input, option) => filterOption={(input, option) =>
@@ -166,43 +179,38 @@ const MetricTrendSection: React.FC<Props> = ({ nodeData }) => {
}} }}
/> />
</FormItem> </FormItem>
</StandardFormRow> */} </StandardFormRow>
{/* <Row> <StandardFormRow key="dimensionFilter" title="维度筛选:">
<Col flex="1 1 200px"> <FormItem name="dimensionFilter">
<Space>
<span>维度下钻: </span>
<Select
style={{ minWidth: 150 }}
options={relationDimensionOptions}
showSearch
filterOption={(input, option) =>
((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
}
mode="multiple"
placeholder="请选择下钻维度"
onChange={(value) => {
const params = { ...queryParams, dimensionGroup: value || [] };
setQueryParams(params);
getMetricTrendData({ ...params });
}}
/>
</Space>
</Col>
</Row>
<Row>
<Col flex="1 1 200px">
<Space>
<span>维度筛选: </span>
<MetricTrendDimensionFilter <MetricTrendDimensionFilter
modelId={nodeData.modelId} modelId={nodeData.modelId}
dimensionOptions={relationDimensionOptions} dimensionOptions={relationDimensionOptions}
onFiltersChange={() => {}} onChange={(filterParams) => {
const {
dimensionBizName: bizName,
dimensionValue: value,
operator,
} = filterParams;
if (bizName && value && operator) {
const params = {
...queryParams,
dimensionFilters: [
{
bizName: 'user_name',
value: ['williamhliu', 'leooonli'],
operator: 'in',
},
],
};
setQueryParams(params);
getMetricTrendData({ ...params });
}
}}
/> />
</Space> </FormItem>
</Col> </StandardFormRow> */}
</Row> */} <StandardFormRow key="metricDate" title="日期区间:">
<Row> <FormItem name="metricDate">
<Col flex="1 1 200px">
<MDatePicker <MDatePicker
initialValues={{ initialValues={{
dateSettingType: 'DYNAMIC', dateSettingType: 'DYNAMIC',
@@ -218,6 +226,7 @@ const MetricTrendSection: React.FC<Props> = ({ nodeData }) => {
}, },
staticParams: {}, staticParams: {},
}} }}
showCurrentDataRangeString={false}
onDateRangeChange={(value, config) => { onDateRangeChange={(value, config) => {
const [startDate, endDate] = value; const [startDate, endDate] = value;
const { dateSettingType, dynamicParams, staticParams } = config; const { dateSettingType, dynamicParams, staticParams } = config;
@@ -232,36 +241,9 @@ const MetricTrendSection: React.FC<Props> = ({ nodeData }) => {
}} }}
disabledAdvanceSetting={true} disabledAdvanceSetting={true}
/> />
{/* <Select </FormItem>
style={{ minWidth: 150 }} </StandardFormRow>
options={relationDimensionOptions} </Form>
showSearch
filterOption={(input, option) =>
((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
}
mode="multiple"
placeholder="请选择下钻维度"
onChange={(value) => {
const params = { ...queryParams, dimensionGroup: value || [] };
setQueryParams(params);
getMetricTrendData({ ...params });
}}
/>
<Select
style={{ minWidth: 150 }}
options={relationDimensionOptions}
showSearch
filterOption={(input, option) =>
((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
}
mode="multiple"
placeholder="请选择筛选维度"
onChange={(value) => {
const params = { ...queryParams, dimensionFilters: value || [] };
setQueryParams(params);
getMetricTrendData({ ...params });
}}
/> */}
</Col> </Col>
<Col flex="0 1"> <Col flex="0 1">
<Button <Button

View File

@@ -303,7 +303,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
}, },
{ {
key: 'batchStop', key: 'batchStop',
label: '批量用', label: '批量用',
}, },
{ {
key: 'batchDelete', key: 'batchDelete',

View File

@@ -252,23 +252,17 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
type="link" type="link"
key="editStatusOfflineBtn" key="editStatusOfflineBtn"
onClick={() => { onClick={() => {
updateDimensionStatus({ queryBatchUpdateStatus([record.id], StatusEnum.OFFLINE);
...record,
status: StatusEnum.OFFLINE,
});
}} }}
> >
</Button> </Button>
) : ( ) : (
<Button <Button
type="link" type="link"
key="editStatusOnlineBtn" key="editStatusOnlineBtn"
onClick={() => { onClick={() => {
updateDimensionStatus({ queryBatchUpdateStatus([record.id], StatusEnum.ONLINE);
...record,
status: StatusEnum.ONLINE,
});
}} }}
> >
@@ -317,7 +311,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
}, },
{ {
key: 'batchStop', key: 'batchStop',
label: '批量用', label: '批量用',
}, },
{ {
key: 'batchDelete', key: 'batchDelete',

View File

@@ -216,23 +216,17 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
type="link" type="link"
key="editStatusOfflineBtn" key="editStatusOfflineBtn"
onClick={() => { onClick={() => {
updateStatus({ queryBatchUpdateStatus([record.id], StatusEnum.OFFLINE);
...record,
status: StatusEnum.OFFLINE,
});
}} }}
> >
</Button> </Button>
) : ( ) : (
<Button <Button
type="link" type="link"
key="editStatusOnlineBtn" key="editStatusOnlineBtn"
onClick={() => { onClick={() => {
updateStatus({ queryBatchUpdateStatus([record.id], StatusEnum.ONLINE);
...record,
status: StatusEnum.ONLINE,
});
}} }}
> >
@@ -281,7 +275,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
}, },
{ {
key: 'batchStop', key: 'batchStop',
label: '批量用', label: '批量用',
}, },
{ {
key: 'batchDelete', key: 'batchDelete',

View File

@@ -156,7 +156,7 @@ const ModelTable: React.FC<Props> = ({
}); });
}} }}
> >
</Button> </Button>
) : ( ) : (
<Button <Button

View File

@@ -34,3 +34,9 @@ export enum StatusEnum {
DELETED = 3, DELETED = 3,
UNKNOWN = -1, UNKNOWN = -1,
} }
export enum OperatorEnum {
EQUAL = '=',
IN = 'IN',
LIKE = 'LIKE',
}