mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-20 06:34:55 +00:00
[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:
@@ -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;
|
||||
Reference in New Issue
Block a user