mirror of
https://github.com/tencentmusic/supersonic.git
synced 2026-01-11 12:41:12 +08:00
* [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 * [improvement][semantic-fe] Redesigning the indicator homepage to incorporate trend charts and table functionality for indicators
144 lines
3.8 KiB
TypeScript
144 lines
3.8 KiB
TypeScript
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={() => {
|
||
// if (autoInit) {
|
||
// loadOptions('', true);
|
||
// } else {
|
||
// 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;
|