[improvement][semantic-fe] Adding batch operations for indicators/dimensions/models (#313)

* [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
This commit is contained in:
tristanliu
2023-11-02 06:11:12 -05:00
committed by GitHub
parent 70784598e1
commit 9f813ca1c0
16 changed files with 1232 additions and 133 deletions

View File

@@ -0,0 +1,139 @@
import React, {
useState,
useRef,
useMemo,
useEffect,
forwardRef,
useImperativeHandle,
Ref,
} from 'react';
import { Select, Spin, Empty } from 'antd';
import debounce from 'lodash/debounce';
// import type { ValueTextType } from '@/constants';
import isFunction from 'lodash/isFunction';
type Props = {
fetchOptions: (...restParams: any[]) => Promise<{ label: any; value: any }[]>;
debounceTimeout?: number;
formatPropsValue?: (value: any) => any;
formatFetchOptionsParams?: (inputValue: string, ctx?: any) => any[];
formatOptions?: (data: any, ctx: any) => any[];
autoInit?: boolean;
disabledSearch?: boolean;
[key: string]: any;
};
type SelectOptions = {
label: string;
} & {
text: string;
} & {
value: any;
};
export type RemoteSelectImperativeHandle = {
emitSearch: (value: string) => void;
};
const { Option } = Select;
const DebounceSelect = forwardRef(
(
{
autoInit = false,
fetchOptions,
debounceTimeout = 500,
formatPropsValue,
formatFetchOptionsParams,
formatOptions,
disabledSearch = false,
...restProps
}: Props,
ref: Ref<any>,
) => {
const props = { ...restProps };
const { ctx, filterOption } = props;
if (isFunction(formatPropsValue)) {
props.value = formatPropsValue(props.value);
}
const [fetching, setFetching] = useState(false);
const [options, setOptions] = useState(props.options || props.source || []);
useImperativeHandle(ref, () => ({
emitSearch: (value: string) => {
loadOptions(value, true);
},
}));
useEffect(() => {
if (autoInit) {
loadOptions('', true);
}
}, []);
useEffect(() => {
setOptions(props.source || []);
}, [props.source]);
const fetchRef = useRef(0);
const loadOptions = (value: string, allowEmptyValue?: boolean) => {
setOptions([]);
if (disabledSearch) {
return;
}
if (!allowEmptyValue && !value) return;
fetchRef.current += 1;
const fetchId = fetchRef.current;
setFetching(true);
const fetchParams = formatFetchOptionsParams ? formatFetchOptionsParams(value, ctx) : [value];
// eslint-disable-next-line prefer-spread
fetchOptions.apply(null, fetchParams).then((newOptions) => {
if (fetchId !== fetchRef.current || !Array.isArray(newOptions)) {
return;
}
let finalOptions = newOptions;
if (formatOptions && isFunction(formatOptions)) {
finalOptions = formatOptions(newOptions, ctx);
}
finalOptions =
filterOption && Array.isArray(finalOptions)
? filterOption?.(finalOptions, ctx)
: finalOptions;
setOptions(finalOptions);
setFetching(false);
});
};
const debounceFetcher = useMemo(() => {
return debounce(loadOptions, debounceTimeout, {
trailing: true,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetchOptions, debounceTimeout]);
return (
<Select
style={{ minWidth: '100px' }}
showSearch
allowClear
mode="multiple"
onClear={() => {
setOptions([]);
}}
onSearch={debounceFetcher}
{...props}
filterOption={false} // 保持对props中filterOption属性的复写不可变更位置
notFoundContent={
fetching ? <Spin size="small" /> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
}
loading={fetching}
>
{options.map((option: SelectOptions) => (
<Option value={option.value} key={option.value}>
{option.text || option.label}
</Option>
))}
</Select>
);
},
);
export default DebounceSelect;