[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 = {
disabledAdvanceSetting?: boolean;
initialValues?: any;
showCurrentDataRangeString?: boolean;
onDateRangeChange: (value: string[], from: any) => void;
onDateRangeTypeChange?: (dateRangeType: DateRangeType) => void;
};
@@ -29,6 +30,7 @@ const { CheckableTag } = Tag;
const MDatePicker: React.FC<Props> = ({
disabledAdvanceSetting,
initialValues,
showCurrentDataRangeString = true,
onDateRangeChange,
onDateRangeTypeChange,
}: any) => {
@@ -420,11 +422,12 @@ const MDatePicker: React.FC<Props> = ({
}
/>
</Popover>
{!(
currentDateSettingType === DateSettingType.STATIC &&
currentDateMode === DateMode.RANGE &&
dateRangeType === DateRangeType.DAY
) && <div style={{ color: '#0e73ff' }}>: {selectedDateRangeString}</div>}
{showCurrentDataRangeString &&
!(
currentDateSettingType === DateSettingType.STATIC &&
currentDateMode === DateMode.RANGE &&
dateRangeType === DateRangeType.DAY
) && <div style={{ color: '#0e73ff' }}>: {selectedDateRangeString}</div>}
</Space>
);
};

View File

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

View File

@@ -1,11 +1,10 @@
import { Form, Input, Select, Space, Row, Col, Switch, Tag } from 'antd';
import StandardFormRow from '@/components/StandardFormRow';
import TagSelect from '@/components/TagSelect';
import { Form, Select, Space, Tag, Button, Tooltip } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
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 { queryDimValue } from '@/pages/SemanticModel/service';
import { OperatorEnum } from '@/pages/SemanticModel/enum';
import { isString } from 'lodash';
import styles from '../style.less';
const FormItem = Form.Item;
@@ -13,24 +12,46 @@ const FormItem = Form.Item;
type Props = {
dimensionOptions: { value: string; label: string }[];
modelId: number;
initFilterValues?: any;
onFiltersChange: (_: any, values: any) => void;
value?: FormData;
onChange?: (value: FormData) => void;
};
export type FormData = {
dimensionBizName: string;
operator: OperatorEnum;
dimensionValue: string | string[];
};
const MetricTrendDimensionFilter: React.FC<Props> = ({
dimensionOptions,
modelId,
initFilterValues = {},
onFiltersChange,
value,
onChange,
}) => {
// const [form] = Form.useForm();
const [form] = Form.useForm();
const dimensionValueSearchRef = useRef<RemoteSelectImperativeHandle>();
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) => {
if (!queryParams.current?.dimensionBizName) {
// return [];
return;
}
const { dimensionBizName } = queryParams.current;
@@ -48,40 +69,82 @@ const MetricTrendDimensionFilter: React.FC<Props> = ({
}
return [];
};
const multipleValueOperator = [OperatorEnum.IN];
return (
<Space>
<Select
style={{ minWidth: 150 }}
options={dimensionOptions}
showSearch
filterOption={(input, option) =>
((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
<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;
}
allowClear
placeholder="请选择筛选维度"
onChange={(value) => {
queryParams.current = { dimensionBizName: value };
if (value) {
dimensionValueSearchRef.current?.emitSearch('');
}
}}
/>
<Tag color="processing" style={{ margin: 0 }}>
=
</Tag>
<RemoteSelect
value={dimensionValue}
onChange={(value) => {
setDimensionValue(value);
}}
ref={dimensionValueSearchRef}
style={{ minWidth: 150 }}
mode={undefined}
placeholder="维度值搜索"
fetchOptions={loadSiteName}
/>
</Space>
if (!multipleValueOperator.includes(operator) && Array.isArray(dimensionValue)) {
const tempDimensionValue = dimensionValue[0];
setFormData({ ...values, dimensionValue: tempDimensionValue });
form.setFieldValue('dimensionValue', tempDimensionValue);
return;
}
setFormData(values);
}}
>
<Space>
<FormItem name="dimensionBizName" noStyle>
<Select
style={{ minWidth: 150 }}
options={dimensionOptions}
showSearch
filterOption={(input, option) =>
((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
}
allowClear
placeholder="请选择筛选维度"
/>
</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>
<FormItem name="dimensionValue" noStyle>
<RemoteSelect
placeholder={formData.dimensionBizName ? '请输入维度值搜索' : '请先选择一个维度'}
ref={dimensionValueSearchRef}
style={{ minWidth: 150 }}
maxTagCount={3}
mode={multipleValueOperator.includes(formData.operator) ? 'multiple' : undefined}
fetchOptions={loadSiteName}
/>
</FormItem>
<Tooltip title={'添加筛选条件'}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={() => {
const formValues = form.getFieldsValue();
onChange?.(formValues);
}}
/>
</Tooltip>
</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,121 +147,103 @@ const MetricTrendSection: React.FC<Props> = ({ nodeData }) => {
return (
<>
<div style={{ marginBottom: 5, display: 'grid', gap: 10 }}>
{/* <StandardFormRow key="showType" title="维度下钻" block>
<FormItem name="showType" valuePropName="checked">
<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 });
}}
/>
</FormItem>
</StandardFormRow> */}
{/* <Row>
<Col flex="1 1 200px">
<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>
<div style={{ marginBottom: 25 }}>
<Row>
<Col flex="1 1 200px">
<Space>
<span>维度筛选: </span>
<MetricTrendDimensionFilter
modelId={nodeData.modelId}
dimensionOptions={relationDimensionOptions}
onFiltersChange={() => {}}
/>
</Space>
</Col>
</Row> */}
<Row>
<Col flex="1 1 200px">
<MDatePicker
initialValues={{
dateSettingType: 'DYNAMIC',
dynamicParams: {
number: 7,
periodType: 'DAYS',
includesCurrentPeriod: true,
shortCutId: 'last7Days',
dateRangeType: 'DAY',
dynamicAdvancedConfigType: 'last',
dateRangeStringDesc: '最近7天',
dateSettingType: DateSettingType.DYNAMIC,
},
staticParams: {},
<Form
layout="inline"
// form={form}
colon={false}
onValuesChange={(value, values) => {
if (value.key) {
return;
}
// handleValuesChange(value, values);
}}
onDateRangeChange={(value, config) => {
const [startDate, endDate] = value;
const { dateSettingType, dynamicParams, staticParams } = config;
let dateField = dateFieldMap[DateRangeType.DAY];
if (DateSettingType.DYNAMIC === dateSettingType) {
dateField = dateFieldMap[dynamicParams.dateRangeType];
}
if (DateSettingType.STATIC === dateSettingType) {
dateField = dateFieldMap[staticParams.dateRangeType];
}
setPeriodDate({ startDate, endDate, dateField });
}}
disabledAdvanceSetting={true}
/>
{/* <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 });
}}
/>
<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 });
}}
/> */}
>
{/* <StandardFormRow key="dimensionSelected" title="维度下钻:">
<FormItem name="dimensionSelected">
<Select
style={{ minWidth: 150, maxWidth: 200 }}
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 });
}}
/>
</FormItem>
</StandardFormRow>
<StandardFormRow key="dimensionFilter" title="维度筛选:">
<FormItem name="dimensionFilter">
<MetricTrendDimensionFilter
modelId={nodeData.modelId}
dimensionOptions={relationDimensionOptions}
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 });
}
}}
/>
</FormItem>
</StandardFormRow> */}
<StandardFormRow key="metricDate" title="日期区间:">
<FormItem name="metricDate">
<MDatePicker
initialValues={{
dateSettingType: 'DYNAMIC',
dynamicParams: {
number: 7,
periodType: 'DAYS',
includesCurrentPeriod: true,
shortCutId: 'last7Days',
dateRangeType: 'DAY',
dynamicAdvancedConfigType: 'last',
dateRangeStringDesc: '最近7天',
dateSettingType: DateSettingType.DYNAMIC,
},
staticParams: {},
}}
showCurrentDataRangeString={false}
onDateRangeChange={(value, config) => {
const [startDate, endDate] = value;
const { dateSettingType, dynamicParams, staticParams } = config;
let dateField = dateFieldMap[DateRangeType.DAY];
if (DateSettingType.DYNAMIC === dateSettingType) {
dateField = dateFieldMap[dynamicParams.dateRangeType];
}
if (DateSettingType.STATIC === dateSettingType) {
dateField = dateFieldMap[staticParams.dateRangeType];
}
setPeriodDate({ startDate, endDate, dateField });
}}
disabledAdvanceSetting={true}
/>
</FormItem>
</StandardFormRow>
</Form>
</Col>
<Col flex="0 1">
<Button

View File

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

View File

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

View File

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

View File

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

View File

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